├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── docs ├── api.html ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap-theme.min.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── bootstrap.min.css.map │ └── docs.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ └── php-express.png ├── index.html └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── docs.js │ ├── jquery-3.4.1.js │ ├── jquery-3.4.1.min.js │ ├── jquery-3.4.1.min.map │ └── run_prettify.js ├── examples ├── 01-basic │ ├── .htaccess │ └── index.php ├── 02-views │ ├── .htaccess │ ├── index.php │ └── views │ │ ├── about.php │ │ └── home.php ├── 03-controllers │ ├── .htaccess │ ├── controllers │ │ └── basic.php │ ├── index.php │ └── views │ │ ├── about.php │ │ └── home.php ├── 04-router │ ├── .htaccess │ └── index.php ├── 05-post │ ├── .htaccess │ └── index.php ├── 06-params │ ├── .htaccess │ └── index.php ├── 07-restful │ ├── .htaccess │ └── index.php └── index.php ├── phpunit.xml.dist ├── src ├── Application.php ├── Request.php ├── Response.php ├── Route.php ├── Router.php └── autoload.php └── tests ├── ApplicationTest.php ├── RequestTest.php ├── ResponseTest.php ├── RouteTest.php └── RouterTest.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://www.paypal.me/Dimitar81 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build-test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | 9 | - name: Install dependencies 10 | uses: php-actions/composer@v6 11 | with: 12 | php_version: '7.2' 13 | 14 | - name: PHPUnit Tests 15 | uses: php-actions/phpunit@v3 16 | with: 17 | php_version: '7.2' 18 | version: 8 19 | 20 | - run: phpunit 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | /vendor/ 3 | /.idea 4 | composer.lock 5 | .phpunit.result.cache 6 | phpunit.xml 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 Dimitar Ivanov 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-express 2 | PHP micro-framework inspired by Express.js 3 | 4 | | Build | GitHub pages | Stable | License | 5 | | --- | --- | --- | --- | 6 | | [![CI](https://github.com/riverside/php-express/actions/workflows/test.yml/badge.svg)](https://github.com/riverside/php-express/actions/workflows/test.yml) | [![pages-build-deployment](https://github.com/riverside/php-express/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/riverside/php-express/actions/workflows/pages/pages-build-deployment) | [![Latest Stable Version](https://poser.pugx.org/riverside/php-express/v/stable)](https://packagist.org/packages/riverside/php-express) | [![License](https://poser.pugx.org/riverside/php-express/license)](https://packagist.org/packages/riverside/php-express) | 7 | 8 | ### Requirements 9 | - PHP >= 7.1 10 | - PHP extensions: 11 | - JSON (`ext-json`) 12 | 13 | ### Installation 14 | If Composer is not installed on your system yet, you may go ahead and install it using this command line: 15 | ``` 16 | $ curl -sS https://getcomposer.org/installer | php 17 | ``` 18 | Next, add the following require entry to the composer.json file in the root of your project. 19 | ```json 20 | { 21 | "require" : { 22 | "riverside/php-express" : "^2.0" 23 | } 24 | } 25 | ``` 26 | Finally, use Composer to install php-express and its dependencies: 27 | ``` 28 | $ php composer.phar install 29 | ``` 30 | ### Routing 31 | ```php 32 | get('/', function ($req, $res) { 36 | $res->send('hello world'); 37 | }); 38 | ``` 39 | #### Route methods 40 | ```php 41 | get('/', function ($req, $res) { 44 | $res->send('GET request to the homepage'); 45 | }); 46 | 47 | // POST method route 48 | $app->post('/', function ($req, $res) { 49 | $res->send('POST request to the homepage'); 50 | }); 51 | ``` 52 | #### Route paths 53 | ```php 54 | get('/', function ($req, $res) { 56 | $res->send('root'); 57 | }); 58 | 59 | $app->get('about', function ($req, $res) { 60 | $res->send('about'); 61 | }); 62 | 63 | $app->get('random.text', function ($req, $res) { 64 | $res->send('random.text'); 65 | }); 66 | ``` 67 | 68 | #### Response methods 69 | | Method | Description | 70 | | ------------------ | --------------------------------- | 71 | | $res->end() | End the response process. | 72 | | $res->json() | Send a JSON response. | 73 | | $res->redirect() | Redirect a request. | 74 | | $res->render() | Render a view template. | 75 | | $res->send() | Send a response of various types. | 76 | | $res->sendStatus() | Set the response status code and send its string representation as the response body. | 77 | 78 | #### $app->route() 79 | ```php 80 | route('/book') 82 | ->get(function ($req, $res) { 83 | $res->send('Get a random book'); 84 | }) 85 | ->post(function ($req, $res) { 86 | $res->send('Add a book'); 87 | }) 88 | ->put(function ($req, $res) { 89 | $res->send('Update the book'); 90 | }); 91 | ``` 92 | 93 | #### Router 94 | ```php 95 | param('uuid', '[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}'); 99 | 100 | $router->get('/', function ($req, $res) { 101 | $res->send('Birds home page'); 102 | }); 103 | 104 | $router->get('about', function ($req, $res) { 105 | $res->send('About birds'); 106 | }); 107 | 108 | $router->get('ticket/:uuid/', function($req, $res) { 109 | echo $req->params['uuid']; 110 | }); 111 | 112 | $router->run(); 113 | ``` 114 | #### Middleware 115 | ```php 116 | $app->use(function($req, $res) { 117 | $res->header('X-Frame-Options', 'DENY'); 118 | $res->header('X-Powered-By', false); 119 | }); 120 | 121 | $app->use('/cors', function($req, $res) { 122 | $res->header('Access-Control-Allow-Origin', '*'); 123 | }); 124 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "riverside/php-express", 3 | "description": "PHP micro-framework inspired by Express.js", 4 | "type": "library", 5 | "version": "2.0.1", 6 | "keywords": ["php", "express", "framework", "router", "application"], 7 | "homepage": "https://github.com/riverside/php-express", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Dimitar Ivanov", 12 | "email": "biggie@abv.bg", 13 | "homepage": "https://twitter.com/DimitarIvanov", 14 | "role": "Developer" 15 | } 16 | ], 17 | "require": { 18 | "ext-json": "*", 19 | "php": ">=7.1" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "8" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Riverside\\Express\\": "src/" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "Riverside\\Express\\Tests\\": "tests/" 32 | } 33 | }, 34 | "scripts": { 35 | "test": "vendor/bin/phpunit" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | .btn-default, 7 | .btn-primary, 8 | .btn-success, 9 | .btn-info, 10 | .btn-warning, 11 | .btn-danger { 12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | } 16 | .btn-default:active, 17 | .btn-primary:active, 18 | .btn-success:active, 19 | .btn-info:active, 20 | .btn-warning:active, 21 | .btn-danger:active, 22 | .btn-default.active, 23 | .btn-primary.active, 24 | .btn-success.active, 25 | .btn-info.active, 26 | .btn-warning.active, 27 | .btn-danger.active { 28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | } 31 | .btn-default.disabled, 32 | .btn-primary.disabled, 33 | .btn-success.disabled, 34 | .btn-info.disabled, 35 | .btn-warning.disabled, 36 | .btn-danger.disabled, 37 | .btn-default[disabled], 38 | .btn-primary[disabled], 39 | .btn-success[disabled], 40 | .btn-info[disabled], 41 | .btn-warning[disabled], 42 | .btn-danger[disabled], 43 | fieldset[disabled] .btn-default, 44 | fieldset[disabled] .btn-primary, 45 | fieldset[disabled] .btn-success, 46 | fieldset[disabled] .btn-info, 47 | fieldset[disabled] .btn-warning, 48 | fieldset[disabled] .btn-danger { 49 | -webkit-box-shadow: none; 50 | box-shadow: none; 51 | } 52 | .btn-default .badge, 53 | .btn-primary .badge, 54 | .btn-success .badge, 55 | .btn-info .badge, 56 | .btn-warning .badge, 57 | .btn-danger .badge { 58 | text-shadow: none; 59 | } 60 | .btn:active, 61 | .btn.active { 62 | background-image: none; 63 | } 64 | .btn-default { 65 | text-shadow: 0 1px 0 #fff; 66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 72 | background-repeat: repeat-x; 73 | border-color: #dbdbdb; 74 | border-color: #ccc; 75 | } 76 | .btn-default:hover, 77 | .btn-default:focus { 78 | background-color: #e0e0e0; 79 | background-position: 0 -15px; 80 | } 81 | .btn-default:active, 82 | .btn-default.active { 83 | background-color: #e0e0e0; 84 | border-color: #dbdbdb; 85 | } 86 | .btn-default.disabled, 87 | .btn-default[disabled], 88 | fieldset[disabled] .btn-default, 89 | .btn-default.disabled:hover, 90 | .btn-default[disabled]:hover, 91 | fieldset[disabled] .btn-default:hover, 92 | .btn-default.disabled:focus, 93 | .btn-default[disabled]:focus, 94 | fieldset[disabled] .btn-default:focus, 95 | .btn-default.disabled.focus, 96 | .btn-default[disabled].focus, 97 | fieldset[disabled] .btn-default.focus, 98 | .btn-default.disabled:active, 99 | .btn-default[disabled]:active, 100 | fieldset[disabled] .btn-default:active, 101 | .btn-default.disabled.active, 102 | .btn-default[disabled].active, 103 | fieldset[disabled] .btn-default.active { 104 | background-color: #e0e0e0; 105 | background-image: none; 106 | } 107 | .btn-primary { 108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 114 | background-repeat: repeat-x; 115 | border-color: #245580; 116 | } 117 | .btn-primary:hover, 118 | .btn-primary:focus { 119 | background-color: #265a88; 120 | background-position: 0 -15px; 121 | } 122 | .btn-primary:active, 123 | .btn-primary.active { 124 | background-color: #265a88; 125 | border-color: #245580; 126 | } 127 | .btn-primary.disabled, 128 | .btn-primary[disabled], 129 | fieldset[disabled] .btn-primary, 130 | .btn-primary.disabled:hover, 131 | .btn-primary[disabled]:hover, 132 | fieldset[disabled] .btn-primary:hover, 133 | .btn-primary.disabled:focus, 134 | .btn-primary[disabled]:focus, 135 | fieldset[disabled] .btn-primary:focus, 136 | .btn-primary.disabled.focus, 137 | .btn-primary[disabled].focus, 138 | fieldset[disabled] .btn-primary.focus, 139 | .btn-primary.disabled:active, 140 | .btn-primary[disabled]:active, 141 | fieldset[disabled] .btn-primary:active, 142 | .btn-primary.disabled.active, 143 | .btn-primary[disabled].active, 144 | fieldset[disabled] .btn-primary.active { 145 | background-color: #265a88; 146 | background-image: none; 147 | } 148 | .btn-success { 149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 155 | background-repeat: repeat-x; 156 | border-color: #3e8f3e; 157 | } 158 | .btn-success:hover, 159 | .btn-success:focus { 160 | background-color: #419641; 161 | background-position: 0 -15px; 162 | } 163 | .btn-success:active, 164 | .btn-success.active { 165 | background-color: #419641; 166 | border-color: #3e8f3e; 167 | } 168 | .btn-success.disabled, 169 | .btn-success[disabled], 170 | fieldset[disabled] .btn-success, 171 | .btn-success.disabled:hover, 172 | .btn-success[disabled]:hover, 173 | fieldset[disabled] .btn-success:hover, 174 | .btn-success.disabled:focus, 175 | .btn-success[disabled]:focus, 176 | fieldset[disabled] .btn-success:focus, 177 | .btn-success.disabled.focus, 178 | .btn-success[disabled].focus, 179 | fieldset[disabled] .btn-success.focus, 180 | .btn-success.disabled:active, 181 | .btn-success[disabled]:active, 182 | fieldset[disabled] .btn-success:active, 183 | .btn-success.disabled.active, 184 | .btn-success[disabled].active, 185 | fieldset[disabled] .btn-success.active { 186 | background-color: #419641; 187 | background-image: none; 188 | } 189 | .btn-info { 190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 196 | background-repeat: repeat-x; 197 | border-color: #28a4c9; 198 | } 199 | .btn-info:hover, 200 | .btn-info:focus { 201 | background-color: #2aabd2; 202 | background-position: 0 -15px; 203 | } 204 | .btn-info:active, 205 | .btn-info.active { 206 | background-color: #2aabd2; 207 | border-color: #28a4c9; 208 | } 209 | .btn-info.disabled, 210 | .btn-info[disabled], 211 | fieldset[disabled] .btn-info, 212 | .btn-info.disabled:hover, 213 | .btn-info[disabled]:hover, 214 | fieldset[disabled] .btn-info:hover, 215 | .btn-info.disabled:focus, 216 | .btn-info[disabled]:focus, 217 | fieldset[disabled] .btn-info:focus, 218 | .btn-info.disabled.focus, 219 | .btn-info[disabled].focus, 220 | fieldset[disabled] .btn-info.focus, 221 | .btn-info.disabled:active, 222 | .btn-info[disabled]:active, 223 | fieldset[disabled] .btn-info:active, 224 | .btn-info.disabled.active, 225 | .btn-info[disabled].active, 226 | fieldset[disabled] .btn-info.active { 227 | background-color: #2aabd2; 228 | background-image: none; 229 | } 230 | .btn-warning { 231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 237 | background-repeat: repeat-x; 238 | border-color: #e38d13; 239 | } 240 | .btn-warning:hover, 241 | .btn-warning:focus { 242 | background-color: #eb9316; 243 | background-position: 0 -15px; 244 | } 245 | .btn-warning:active, 246 | .btn-warning.active { 247 | background-color: #eb9316; 248 | border-color: #e38d13; 249 | } 250 | .btn-warning.disabled, 251 | .btn-warning[disabled], 252 | fieldset[disabled] .btn-warning, 253 | .btn-warning.disabled:hover, 254 | .btn-warning[disabled]:hover, 255 | fieldset[disabled] .btn-warning:hover, 256 | .btn-warning.disabled:focus, 257 | .btn-warning[disabled]:focus, 258 | fieldset[disabled] .btn-warning:focus, 259 | .btn-warning.disabled.focus, 260 | .btn-warning[disabled].focus, 261 | fieldset[disabled] .btn-warning.focus, 262 | .btn-warning.disabled:active, 263 | .btn-warning[disabled]:active, 264 | fieldset[disabled] .btn-warning:active, 265 | .btn-warning.disabled.active, 266 | .btn-warning[disabled].active, 267 | fieldset[disabled] .btn-warning.active { 268 | background-color: #eb9316; 269 | background-image: none; 270 | } 271 | .btn-danger { 272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 278 | background-repeat: repeat-x; 279 | border-color: #b92c28; 280 | } 281 | .btn-danger:hover, 282 | .btn-danger:focus { 283 | background-color: #c12e2a; 284 | background-position: 0 -15px; 285 | } 286 | .btn-danger:active, 287 | .btn-danger.active { 288 | background-color: #c12e2a; 289 | border-color: #b92c28; 290 | } 291 | .btn-danger.disabled, 292 | .btn-danger[disabled], 293 | fieldset[disabled] .btn-danger, 294 | .btn-danger.disabled:hover, 295 | .btn-danger[disabled]:hover, 296 | fieldset[disabled] .btn-danger:hover, 297 | .btn-danger.disabled:focus, 298 | .btn-danger[disabled]:focus, 299 | fieldset[disabled] .btn-danger:focus, 300 | .btn-danger.disabled.focus, 301 | .btn-danger[disabled].focus, 302 | fieldset[disabled] .btn-danger.focus, 303 | .btn-danger.disabled:active, 304 | .btn-danger[disabled]:active, 305 | fieldset[disabled] .btn-danger:active, 306 | .btn-danger.disabled.active, 307 | .btn-danger[disabled].active, 308 | fieldset[disabled] .btn-danger.active { 309 | background-color: #c12e2a; 310 | background-image: none; 311 | } 312 | .thumbnail, 313 | .img-thumbnail { 314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 316 | } 317 | .dropdown-menu > li > a:hover, 318 | .dropdown-menu > li > a:focus { 319 | background-color: #e8e8e8; 320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 325 | background-repeat: repeat-x; 326 | } 327 | .dropdown-menu > .active > a, 328 | .dropdown-menu > .active > a:hover, 329 | .dropdown-menu > .active > a:focus { 330 | background-color: #2e6da4; 331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 336 | background-repeat: repeat-x; 337 | } 338 | .navbar-default { 339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 345 | background-repeat: repeat-x; 346 | border-radius: 4px; 347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 349 | } 350 | .navbar-default .navbar-nav > .open > a, 351 | .navbar-default .navbar-nav > .active > a { 352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 357 | background-repeat: repeat-x; 358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 360 | } 361 | .navbar-brand, 362 | .navbar-nav > li > a { 363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 364 | } 365 | .navbar-inverse { 366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 372 | background-repeat: repeat-x; 373 | border-radius: 4px; 374 | } 375 | .navbar-inverse .navbar-nav > .open > a, 376 | .navbar-inverse .navbar-nav > .active > a { 377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 382 | background-repeat: repeat-x; 383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 385 | } 386 | .navbar-inverse .navbar-brand, 387 | .navbar-inverse .navbar-nav > li > a { 388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 389 | } 390 | .navbar-static-top, 391 | .navbar-fixed-top, 392 | .navbar-fixed-bottom { 393 | border-radius: 0; 394 | } 395 | @media (max-width: 767px) { 396 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 399 | color: #fff; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | } 408 | .alert { 409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 412 | } 413 | .alert-success { 414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 419 | background-repeat: repeat-x; 420 | border-color: #b2dba1; 421 | } 422 | .alert-info { 423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 428 | background-repeat: repeat-x; 429 | border-color: #9acfea; 430 | } 431 | .alert-warning { 432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #f5e79e; 439 | } 440 | .alert-danger { 441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 446 | background-repeat: repeat-x; 447 | border-color: #dca7a7; 448 | } 449 | .progress { 450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .progress-bar { 458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .progress-bar-success { 466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 471 | background-repeat: repeat-x; 472 | } 473 | .progress-bar-info { 474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 479 | background-repeat: repeat-x; 480 | } 481 | .progress-bar-warning { 482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 487 | background-repeat: repeat-x; 488 | } 489 | .progress-bar-danger { 490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 495 | background-repeat: repeat-x; 496 | } 497 | .progress-bar-striped { 498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 501 | } 502 | .list-group { 503 | border-radius: 4px; 504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 506 | } 507 | .list-group-item.active, 508 | .list-group-item.active:hover, 509 | .list-group-item.active:focus { 510 | text-shadow: 0 -1px 0 #286090; 511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 516 | background-repeat: repeat-x; 517 | border-color: #2b669a; 518 | } 519 | .list-group-item.active .badge, 520 | .list-group-item.active:hover .badge, 521 | .list-group-item.active:focus .badge { 522 | text-shadow: none; 523 | } 524 | .panel { 525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 527 | } 528 | .panel-default > .panel-heading { 529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 534 | background-repeat: repeat-x; 535 | } 536 | .panel-primary > .panel-heading { 537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 542 | background-repeat: repeat-x; 543 | } 544 | .panel-success > .panel-heading { 545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 550 | background-repeat: repeat-x; 551 | } 552 | .panel-info > .panel-heading { 553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 558 | background-repeat: repeat-x; 559 | } 560 | .panel-warning > .panel-heading { 561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 566 | background-repeat: repeat-x; 567 | } 568 | .panel-danger > .panel-heading { 569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 574 | background-repeat: repeat-x; 575 | } 576 | .well { 577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 582 | background-repeat: repeat-x; 583 | border-color: #dcdcdc; 584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 586 | } 587 | /*# sourceMappingURL=bootstrap-theme.css.map */ 588 | -------------------------------------------------------------------------------- /docs/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} 6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */ -------------------------------------------------------------------------------- /docs/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} -------------------------------------------------------------------------------- /docs/css/docs.css: -------------------------------------------------------------------------------- 1 | #menu { 2 | position: fixed; 3 | top: 71px; 4 | left: calc((100% - 1170px)/2); 5 | overflow-y: auto; 6 | } 7 | #menu.nav>li>a { 8 | padding: 5px 15px; 9 | } 10 | #menu.nav .nav { 11 | height: 0; 12 | overflow: hidden; 13 | } 14 | #menu.nav .nav>li>a, 15 | #menu.nav .nav>li>span { 16 | padding: 5px 15px 5px 30px; 17 | } 18 | #menu.nav .nav>li>span { 19 | font-weight: 700; 20 | } 21 | #menu.nav .nav>li.active { 22 | background-color: #ccc; 23 | } 24 | #menu.nav .nav.active{ 25 | height: auto; 26 | } 27 | #main { 28 | margin-top: 71px; 29 | } 30 | pre.prettyprint { 31 | padding: 9.5px !important; 32 | } 33 | section { 34 | margin: 20px 0; 35 | } -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverside/php-express/dfce38d446fbb0ec0b47c7f6481e283d8ed7be9d/docs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverside/php-express/dfce38d446fbb0ec0b47c7f6481e283d8ed7be9d/docs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverside/php-express/dfce38d446fbb0ec0b47c7f6481e283d8ed7be9d/docs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverside/php-express/dfce38d446fbb0ec0b47c7f6481e283d8ed7be9d/docs/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/img/php-express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverside/php-express/dfce38d446fbb0ec0b47c7f6481e283d8ed7be9d/docs/img/php-express.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PHP Express micro-framework 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 59 | 60 |
61 |
62 |
63 |

php-express

64 |

PHP micro-framework inspired by Express.js

65 |
66 |

Requirements

67 |

PHP >= 7.1

68 |
69 |
70 |

Installation

71 |

If Composer is not installed on your system yet, you may go ahead and install it using this command line:

72 |
$ curl -sS https://getcomposer.org/installer | php
73 |

Next, add the following require entry to the composer.json file in the root of your project.

74 |
{
 75 |     "require" : {
 76 |         "riverside/php-express" : "^2.0"
 77 |     }
 78 | }
79 |

Finally, use Composer to install php-express and its dependencies:

80 |
$ php composer.phar install
81 |
82 |
83 |

Downloads

84 |

The project is hosted on GitHub.

85 |

86 | Download .zip file 87 | Download .tar.gz file 88 |

89 |
90 |
91 |

Author

92 |

PHP Express is developed by Dimitar Ivanov. You can find me on Twitter 93 | or GitHub.

94 |
95 |
96 |

License

97 |

The project is licensed under the MIT license.

98 |
99 |
100 |
101 |
102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /docs/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(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]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",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(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.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},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.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 in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.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").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.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)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(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]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).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(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /docs/js/docs.js: -------------------------------------------------------------------------------- 1 | (function ($, window, document, undefined) { 2 | $(function () { 3 | 4 | $(document).on("click", "#menu>li>a", function (e) { 5 | e.preventDefault(); 6 | var $li = $(this).closest("li"); 7 | if ($li.hasClass("active")) { 8 | $li.removeClass("active").find(".nav").removeClass("active"); 9 | } else { 10 | $li.addClass("active").find(".nav").addClass("active"); 11 | } 12 | $("#menu>li").not($li).removeClass("active").each(function () { 13 | $(this).find(".nav").removeClass("active").find("li").removeClass("active"); 14 | }); 15 | window.location.hash = this.getAttribute("href"); 16 | }).on("click", "#menu .nav a", function (e) { 17 | e.preventDefault(); 18 | var $li = $(this).closest("li"); 19 | if ($li.hasClass("active")) { 20 | $li.removeClass("active"); 21 | } else { 22 | $li.addClass("active").siblings("li").removeClass("active"); 23 | } 24 | window.location.hash = this.getAttribute("href"); 25 | scrollTo(this.getAttribute("href")); 26 | }); 27 | 28 | function scrollTo(selector) { 29 | $("html,body").animate({ 30 | scrollTop: $(selector).offset().top - 71 31 | }, 'fast'); 32 | } 33 | }); 34 | })(jQuery, window, document); -------------------------------------------------------------------------------- /docs/js/run_prettify.js: -------------------------------------------------------------------------------- 1 | !function(){/* 2 | 3 | Copyright (C) 2013 Google Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | Copyright (C) 2006 Google Inc. 18 | 19 | Licensed under the Apache License, Version 2.0 (the "License"); 20 | you may not use this file except in compliance with the License. 21 | You may obtain a copy of the License at 22 | 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | Unless required by applicable law or agreed to in writing, software 26 | distributed under the License is distributed on an "AS IS" BASIS, 27 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | See the License for the specific language governing permissions and 29 | limitations under the License. 30 | */ 31 | (function(){function aa(g){function r(){try{L.doScroll("left")}catch(ba){k.setTimeout(r,50);return}x("poll")}function x(r){if("readystatechange"!=r.type||"complete"==z.readyState)("load"==r.type?k:z)[B](n+r.type,x,!1),!l&&(l=!0)&&g.call(k,r.type||r)}var X=z.addEventListener,l=!1,E=!0,v=X?"addEventListener":"attachEvent",B=X?"removeEventListener":"detachEvent",n=X?"":"on";if("complete"==z.readyState)g.call(k,"lazy");else{if(z.createEventObject&&L.doScroll){try{E=!k.frameElement}catch(ba){}E&&r()}z[v](n+ 32 | "DOMContentLoaded",x,!1);z[v](n+"readystatechange",x,!1);k[v](n+"load",x,!1)}}function T(){U&&aa(function(){var g=M.length;ca(g?function(){for(var r=0;r=c?parseInt(e.substring(1),8):"u"===c||"x"===c?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e); 36 | return"\\"===e||"-"===e||"]"===e||"^"===e?"\\"+e:e}function c(e){var c=e.substring(1,e.length-1).match(RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));e=[];var a="^"===c[0],b=["["];a&&b.push("^");for(var a=a?1:0,h=c.length;ap||122p||90p||122m[0]&&(m[1]+1>m[0]&&b.push("-"),b.push(f(m[1])));b.push("]");return b.join("")}function g(e){for(var a=e.source.match(RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)", 38 | "g")),b=a.length,d=[],h=0,m=0;h/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/, 45 | null]));if(c=a.regexLiterals){var g=(c=1|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+c+"])(?:[^/\\x5B\\x5C"+c+"]|\\x5C"+g+"|\\x5B(?:[^\\x5C\\x5D"+c+"]|\\x5C"+g+")*(?:\\x5D|$))+/")+")")])}(c=a.types)&&f.push(["typ",c]);c=(""+a.keywords).replace(/^ | $/g,"");c.length&&f.push(["kwd", 46 | new RegExp("^(?:"+c.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);c="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(c+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i,null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(c),null]);return E(d,f)}function B(a,d,f){function c(a){var b= 47 | a.nodeType;if(1==b&&!r.test(a.className))if("br"===a.nodeName.toLowerCase())g(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)c(a);else if((3==b||4==b)&&f){var e=a.nodeValue,d=e.match(n);d&&(b=e.substring(0,d.index),a.nodeValue=b,(e=e.substring(d.index+d[0].length))&&a.parentNode.insertBefore(q.createTextNode(e),a.nextSibling),g(a),b||a.parentNode.removeChild(a))}}function g(a){function c(a,b){var e=b?a.cloneNode(!1):a,p=a.parentNode;if(p){var p=c(p,1),d=a.nextSibling; 48 | p.appendChild(e);for(var f=d;f;f=d)d=f.nextSibling,p.appendChild(f)}return e}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=c(a.nextSibling,0);for(var e;(e=a.parentNode)&&1===e.nodeType;)a=e;b.push(a)}for(var r=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,q=a.ownerDocument,k=q.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var b=[k],t=0;t=+g[1],d=/\n/g,r=a.a,k=r.length,f=0,q=a.c,n=q.length,c=0,b=a.g,t=b.length,v=0;b[t]=k;var u,e;for(e=u=0;e=m&&(c+=2);f>=p&&(v+=2)}}finally{h&&(h.style.display=a)}}catch(y){Q.console&&console.log(y&&y.stack||y)}}var Q="undefined"!==typeof window?window:{},J=["break,continue,do,else,for,if,return,while"],K=[[J,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 52 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],R=[K,"alignas,alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],L=[K,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], 53 | M=[K,"abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface,internal,into,is,join,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,value,var,virtual,where,yield"],K=[K,"abstract,async,await,constructor,debugger,enum,eval,export,from,function,get,import,implements,instanceof,interface,let,null,of,set,undefined,var,with,yield,Infinity,NaN"], 54 | N=[J,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],O=[J,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],J=[J,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],P=/^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/, 55 | S=/\S/,T=v({keywords:[R,M,L,K,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",N,O,J],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),V={};n(T,["default-code"]);n(E([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-", 56 | /^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),"default-markup htm html mxml xhtml xml xsl".split(" "));n(E([["pln",/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/], 57 | ["pun",/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);n(E([],[["atv",/^[\s\S]+/]]),["uq.val"]);n(v({keywords:R,hashComments:!0,cStyleComments:!0,types:P}),"c cc cpp cxx cyc m".split(" "));n(v({keywords:"null,true,false"}),["json"]);n(v({keywords:M,hashComments:!0,cStyleComments:!0, 58 | verbatimStrings:!0,types:P}),["cs"]);n(v({keywords:L,cStyleComments:!0}),["java"]);n(v({keywords:J,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);n(v({keywords:N,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);n(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}), 59 | ["perl","pl","pm"]);n(v({keywords:O,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);n(v({keywords:K,cStyleComments:!0,regexLiterals:!0}),["javascript","js","ts","typescript"]);n(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);n(E([],[["str",/^[\s\S]+/]]), 60 | ["regex"]);var U=Q.PR={createSimpleLexer:E,registerLangHandler:n,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,f){f=f||!1;d=d||null;var c=document.createElement("div");c.innerHTML="
"+a+"
";c=c.firstChild;f&&B(c,f,!0);H({j:d,m:f,h:c,l:1,a:null,i:null,c:null,g:null}); 61 | return c.innerHTML},prettyPrint:g=function(a,d){function f(){for(var c=Q.PR_SHOULD_USE_CONTINUATION?b.now()+250:Infinity;t 2 | RewriteEngine On 3 | 4 | RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) 5 | RewriteRule .* - [F] 6 | 7 | RewriteCond %{ENV:REDIRECT_STATUS} 200 8 | RewriteRule .* - [L] 9 | 10 | RewriteCond %{REQUEST_FILENAME} !-f 11 | RewriteCond %{REQUEST_FILENAME} !-d 12 | RewriteRule ^(.*)$ index.php?_path_=$1 [QSA,L] 13 | -------------------------------------------------------------------------------- /examples/01-basic/index.php: -------------------------------------------------------------------------------- 1 | get('/', function ($req, $res) { 7 | $res->send('

Page 1

Next »'); 8 | }); 9 | 10 | $app->get('page-2.html', function ($req, $res) { 11 | $res->send('

Page 2

« Prev'); 12 | }); 13 | 14 | $app->run(); -------------------------------------------------------------------------------- /examples/02-views/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | 4 | RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) 5 | RewriteRule .* - [F] 6 | 7 | RewriteCond %{ENV:REDIRECT_STATUS} 200 8 | RewriteRule .* - [L] 9 | 10 | RewriteCond %{REQUEST_FILENAME} !-f 11 | RewriteCond %{REQUEST_FILENAME} !-d 12 | RewriteRule ^(.*)$ index.php?_path_=$1 [QSA,L] 13 | -------------------------------------------------------------------------------- /examples/02-views/index.php: -------------------------------------------------------------------------------- 1 | set("views", __DIR__ . "/views"); 6 | 7 | $app->get('/', function ($req, $res) { 8 | $res->render('home'); 9 | }); 10 | 11 | $app->get('about.html', function ($req, $res) { 12 | $res->render('about'); 13 | }); 14 | 15 | $app->run(); -------------------------------------------------------------------------------- /examples/02-views/views/about.php: -------------------------------------------------------------------------------- 1 |

About page

2 | 3 | Home -------------------------------------------------------------------------------- /examples/02-views/views/home.php: -------------------------------------------------------------------------------- 1 |

Home page

2 | 3 | About -------------------------------------------------------------------------------- /examples/03-controllers/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | 4 | RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) 5 | RewriteRule .* - [F] 6 | 7 | RewriteCond %{ENV:REDIRECT_STATUS} 200 8 | RewriteRule .* - [L] 9 | 10 | RewriteCond %{REQUEST_FILENAME} !-f 11 | RewriteCond %{REQUEST_FILENAME} !-d 12 | RewriteRule ^(.*)$ index.php?_path_=$1 [QSA,L] 13 | -------------------------------------------------------------------------------- /examples/03-controllers/controllers/basic.php: -------------------------------------------------------------------------------- 1 | render('about'); 9 | } 10 | 11 | public function home($req, $res) 12 | { 13 | $res->render('home'); 14 | } 15 | } -------------------------------------------------------------------------------- /examples/03-controllers/index.php: -------------------------------------------------------------------------------- 1 | set("views", __DIR__ . "/views"); 14 | 15 | $app->get('/', '\controllers\basic@home'); 16 | 17 | $app->get('about.html', '\controllers\basic@about'); 18 | 19 | $app->run(); -------------------------------------------------------------------------------- /examples/03-controllers/views/about.php: -------------------------------------------------------------------------------- 1 |

About page

2 | 3 | Home -------------------------------------------------------------------------------- /examples/03-controllers/views/home.php: -------------------------------------------------------------------------------- 1 |

Home page

2 | 3 | About -------------------------------------------------------------------------------- /examples/04-router/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | 4 | RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) 5 | RewriteRule .* - [F] 6 | 7 | RewriteCond %{ENV:REDIRECT_STATUS} 200 8 | RewriteRule .* - [L] 9 | 10 | RewriteCond %{REQUEST_FILENAME} !-f 11 | RewriteCond %{REQUEST_FILENAME} !-d 12 | RewriteRule ^(.*)$ index.php?_path_=$1 [QSA,L] 13 | -------------------------------------------------------------------------------- /examples/04-router/index.php: -------------------------------------------------------------------------------- 1 | get('/', function ($req, $res) { 7 | $res->send('

Route 1

Next »'); 8 | }); 9 | 10 | $router->get('route-2.html', function ($req, $res) { 11 | $res->send('

Route 2

« Prev'); 12 | }); 13 | 14 | $router->run(); -------------------------------------------------------------------------------- /examples/05-post/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | 4 | RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) 5 | RewriteRule .* - [F] 6 | 7 | RewriteCond %{ENV:REDIRECT_STATUS} 200 8 | RewriteRule .* - [L] 9 | 10 | RewriteCond %{REQUEST_FILENAME} !-f 11 | RewriteCond %{REQUEST_FILENAME} !-d 12 | RewriteRule ^(.*)$ index.php?_path_=$1 [QSA,L] 13 | -------------------------------------------------------------------------------- /examples/05-post/index.php: -------------------------------------------------------------------------------- 1 | get('/', function ($req, $res) { 7 | $html = << 9 |

10 | 11 | 12 |

13 |

14 | 15 | 16 |

17 | 18 | 19 | EEE; 20 | $res->send($html); 21 | }); 22 | 23 | $router->post('/', function ($req, $res) { 24 | $res->send('
'.print_r($_POST, true));
25 | });
26 | 
27 | $router->run();


--------------------------------------------------------------------------------
/examples/06-params/.htaccess:
--------------------------------------------------------------------------------
 1 | 
 2 |   RewriteEngine On
 3 | 
 4 |   RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
 5 |   RewriteRule .* - [F]
 6 | 
 7 |   RewriteCond %{ENV:REDIRECT_STATUS} 200
 8 |   RewriteRule .* - [L]
 9 | 
10 |   RewriteCond %{REQUEST_FILENAME} !-f
11 |   RewriteCond %{REQUEST_FILENAME} !-d
12 |   RewriteRule ^(.*)$ index.php?_path_=$1 [QSA,L]
13 | 


--------------------------------------------------------------------------------
/examples/06-params/index.php:
--------------------------------------------------------------------------------
 1 | param('username', '[a-zA-Z\d]{1,32}');
 7 | $app->param('wildcard', '.*');
 8 | 
 9 | $app->get('/', function ($req, $res) {
10 |     $link = 'user/riverside/pictures/123456/Test-98_7/ea416ed0759d46a8de58f63a59077499';
11 |     $res->send('

Pattern: user/:username/pictures/:id/:wildcard/:hash

Link: '.$link.'

'); 12 | }); 13 | 14 | $app->get('user/:username/pictures/:id/:wildcard/:hash', function ($req, $res) { 15 | $res->send('« Back
'.print_r($req->params, true));
16 | });
17 | 
18 | $app->run();


--------------------------------------------------------------------------------
/examples/07-restful/.htaccess:
--------------------------------------------------------------------------------
 1 | 
 2 |   RewriteEngine On
 3 | 
 4 |   RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
 5 |   RewriteRule .* - [F]
 6 | 
 7 |   RewriteCond %{ENV:REDIRECT_STATUS} 200
 8 |   RewriteRule .* - [L]
 9 | 
10 |   RewriteCond %{REQUEST_FILENAME} !-f
11 |   RewriteCond %{REQUEST_FILENAME} !-d
12 |   RewriteRule ^(.*)$ index.php?_path_=$1 [QSA,L]
13 | 
14 | 


--------------------------------------------------------------------------------
/examples/07-restful/index.php:
--------------------------------------------------------------------------------
 1 | method == 'HEAD')
 8 |     {
 9 |         $res->end();
10 |     }
11 |     $res->json([
12 |         'method' => $req->method,
13 |         'content-type' => $req->get("content-type"),
14 |         'params' => $req->params,
15 |         'body' => $req->body,
16 |     ]);
17 | };
18 | 
19 | $app->get('/', function($req, $res) {
20 |     $html = <<
22 |     
23 |     
24 |     
25 |     
26 |     
27 |     
28 |     
29 | 
30 | 
31 | 
77 | EEE;
78 |     $res->send($html);
79 | });
80 | $app->get('contact/:id', $testVerbs);
81 | $app->head('contact/:id', $testVerbs);
82 | $app->post('contact/:id', $testVerbs);
83 | $app->put('contact/:id', $testVerbs);
84 | $app->patch('contact/:id', $testVerbs);
85 | $app->delete('contact/:id', $testVerbs);
86 | 
87 | $app->run();
88 | 


--------------------------------------------------------------------------------
/examples/index.php:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     php-express examples
 5 |     
 6 | 
 7 | 
 8 | 
 9 |     

Examples

10 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./tests/ 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Application.php: -------------------------------------------------------------------------------- 1 | 'views', 23 | 'template' => 'layouts/default' 24 | ); 25 | 26 | /** 27 | * @var array 28 | */ 29 | protected $locals = array(); 30 | 31 | /** 32 | * @var Request 33 | */ 34 | protected $request; 35 | 36 | /** 37 | * @var Response 38 | */ 39 | protected $response; 40 | 41 | /** 42 | * @var array 43 | */ 44 | protected $patterns = array( 45 | ':id' => '(\d+)', 46 | ':hash' => '([a-f\d+]{32})' 47 | ); 48 | 49 | /** 50 | * Application constructor. 51 | */ 52 | public function __construct() 53 | { 54 | $this->request = new Request($this); 55 | $this->response = new Response($this); 56 | } 57 | 58 | /** 59 | * Return an instance of response object. 60 | * 61 | * @return Response 62 | */ 63 | public function getResponse(): Response 64 | { 65 | return $this->response; 66 | } 67 | 68 | /** 69 | * Return an instance of request object. 70 | * 71 | * @return Request 72 | */ 73 | public function getRequest(): Request 74 | { 75 | return $this->request; 76 | } 77 | 78 | /** 79 | * Sets the Boolean setting name to false 80 | * 81 | * @param string $name 82 | * @return Application 83 | */ 84 | public function disable(string $name): Application 85 | { 86 | return $this->set($name, false); 87 | } 88 | 89 | /** 90 | * Returns true if the Boolean setting name is disabled (false) 91 | * 92 | * @param string $name 93 | * @return bool 94 | */ 95 | public function disabled(string $name): bool 96 | { 97 | return !$this->set($name); 98 | } 99 | 100 | /** 101 | * Sets the Boolean setting name to true 102 | * 103 | * @param string $name 104 | * @return Application 105 | */ 106 | public function enable(string $name): Application 107 | { 108 | return $this->set($name, true); 109 | } 110 | 111 | /** 112 | * Returns true if the setting name is enabled (true) 113 | * 114 | * @param string $name 115 | * @return bool 116 | */ 117 | public function enabled(string $name): bool 118 | { 119 | return (bool) $this->set($name); 120 | } 121 | 122 | /** 123 | * Attempt to create an instance of Router, if such not already exists 124 | * 125 | * @return Application 126 | */ 127 | protected function lazyrouter(): Application 128 | { 129 | if (!$this->router) 130 | { 131 | $this->router = new Router($this); 132 | } 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * Get/set local variables to use within the application. 139 | * 140 | * @param string|null $name 141 | * @param null $value 142 | * @return Application|array|mixed|null 143 | */ 144 | public function local(string $name = null, $value = null) 145 | { 146 | $num = func_num_args(); 147 | 148 | if ($num == 0) 149 | { 150 | // getter 151 | return $this->locals; 152 | } 153 | 154 | if ($num == 1) { 155 | // getter 156 | return array_key_exists($name, $this->locals) 157 | ? $this->locals[$name] 158 | : null; 159 | } 160 | 161 | // setter 162 | $this->locals[$name] = $value; 163 | 164 | return $this; 165 | } 166 | 167 | /** 168 | * Add custom parameters to use in a route path. 169 | * 170 | * @param string $name 171 | * @param string|null $regex 172 | * @return Application 173 | */ 174 | public function param(string $name, string $regex = null): Application 175 | { 176 | $this->patterns[":$name"] = $regex ? "($regex)" : '(.*)'; 177 | 178 | return $this; 179 | } 180 | 181 | /** 182 | * Returns the rendered HTML of a view. It accepts an optional parameter that is an array containing local variables 183 | * for the view. It is like $res->render(), except it cannot send the rendered view to the client on its own. 184 | * 185 | * @param string $view 186 | * @param array|null $locals 187 | */ 188 | public function render(string $view, array $locals=null): void 189 | { 190 | if ($locals) 191 | { 192 | extract($locals); 193 | } 194 | $layout = sprintf("%s/%s.php", $this->set("views"), $view); 195 | $_template = sprintf("%s/%s.php", $this->set("views"), $this->set("template")); 196 | if (is_file($_template)) 197 | { 198 | include $_template; 199 | } else { 200 | include $layout; 201 | } 202 | } 203 | 204 | /** 205 | * Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. 206 | * Use $app->route() to avoid duplicate route names (and thus typo errors). 207 | * 208 | * @param string $path 209 | * @return Route 210 | */ 211 | public function route(string $path): Route 212 | { 213 | $this->lazyrouter(); 214 | 215 | $route = $this->router->route($path); 216 | 217 | return $route; 218 | } 219 | 220 | /** 221 | * Run the application. 222 | */ 223 | public function run(): void 224 | { 225 | if (!$this->router) 226 | { 227 | return; 228 | } 229 | 230 | foreach ($this->router->getRoutes() as $route) 231 | { 232 | $match1 = in_array($route->getMethod(), array('all', 'use', strtolower($this->request->method))); 233 | if (!$match1) 234 | { 235 | continue; 236 | } 237 | 238 | $use = $route->getMethod() == "use"; 239 | $found = 0; 240 | 241 | $pattern = sprintf("#^%s$#", str_replace(array_keys($this->patterns), array_values($this->patterns), $route->getPath())); 242 | if (!$use) 243 | { 244 | $match2 = null; 245 | if (!preg_match($pattern, $this->request->path, $match2)) 246 | { 247 | continue; 248 | } else { 249 | $found = 1; 250 | //array_shift($match2); 251 | $this->request->route = $route->getPath(); 252 | $this->setParams(); 253 | } 254 | } else { 255 | if (!($route->getPath() == '*' || preg_match($pattern, $this->request->path))) 256 | { 257 | continue; 258 | } 259 | } 260 | 261 | foreach ($route->getCallback() as $callback) 262 | { 263 | $found = 1; 264 | if (is_array($callback)) 265 | { 266 | foreach ($callback as $arg) 267 | { 268 | $route->dispatch($arg, $use); 269 | } 270 | } else { 271 | $route->dispatch($callback, $use); 272 | } 273 | } 274 | 275 | if ($found && !$use) 276 | { 277 | break; 278 | } 279 | } 280 | } 281 | 282 | /** 283 | * Set params 284 | * 285 | * @return Application 286 | */ 287 | protected function setParams(): Application 288 | { 289 | $route_parts = explode('/', $this->request->route); 290 | $path_parts = explode('/', $this->request->path); 291 | foreach ($route_parts as $key => $value) 292 | { 293 | if (strpos($value, ':') === 0) 294 | { 295 | $this->request->params[substr($value, 1)] = $path_parts[$key]; 296 | } 297 | } 298 | 299 | return $this; 300 | } 301 | 302 | /** 303 | * Assigns setting name to value. 304 | * 305 | * @param string $name 306 | * @param mixed|null $value 307 | * @return Application|mixed|null 308 | */ 309 | public function set(string $name, $value = null) 310 | { 311 | if (func_num_args() == 1) 312 | { 313 | // getter 314 | return array_key_exists($name, $this->settings) 315 | ? $this->settings[$name] 316 | : null; 317 | } 318 | 319 | // setter 320 | $this->settings[$name] = $value; 321 | 322 | return $this; 323 | } 324 | 325 | /** 326 | * Mounts the specified middleware function at the specified path: the middleware function is executed when 327 | * the base of the requested path matches path. 328 | * 329 | * @return Application 330 | */ 331 | public function use(): Application 332 | { 333 | $num_args = func_num_args(); 334 | if (!$num_args) 335 | { 336 | return $this; 337 | } 338 | 339 | $args = func_get_args(); 340 | switch ($num_args) 341 | { 342 | case 1: 343 | $path = '*'; 344 | $offset = 0; 345 | break; 346 | default: 347 | $path = $args[0]; 348 | $offset = 1; 349 | } 350 | 351 | $arguments = array_slice($args, $offset); 352 | 353 | $this->lazyrouter(); 354 | 355 | $route = $this->router->route($path); 356 | 357 | call_user_func_array(array($route, 'use'), $arguments); 358 | 359 | return $this; 360 | } 361 | 362 | /** 363 | * Routes HTTP (verb) requests to the specified path with the specified callback function. 364 | * 365 | * @param string $name Accepts: delete, get, head, options, patch, post, put, all 366 | * @param array $arguments 367 | * @return Application 368 | */ 369 | public function __call(string $name, array $arguments): Application 370 | { 371 | $methods = array_merge(Route::METHODS, array('all')); 372 | if (!in_array($name, $methods)) 373 | { 374 | trigger_error("The '$name' method is not defined", E_USER_WARNING); 375 | } 376 | 377 | $this->lazyrouter(); 378 | 379 | $route = $this->router->route($arguments[0]); 380 | $route->setMethod($name); 381 | $route->setCallback(array_slice($arguments, 1)); 382 | 383 | //call_user_func_array(array($route, $name), array_slice($arguments, 1)); 384 | 385 | return $this; 386 | } 387 | } -------------------------------------------------------------------------------- /src/Request.php: -------------------------------------------------------------------------------- 1 | app = $app; 154 | $this->body = &$_POST; 155 | $this->cookies = &$_COOKIE; 156 | $this->files = &$_FILES; 157 | $this->hostname = $_SERVER['HTTP_HOST']; 158 | $this->ip = $_SERVER['REMOTE_ADDR']; 159 | $this->method = strtoupper($_SERVER['REQUEST_METHOD']); 160 | $this->originalUrl = $_SERVER['REQUEST_URI']; 161 | $this->path = $path; 162 | $this->port = (int) $_SERVER['SERVER_PORT']; 163 | $this->protocol = $_SERVER['SERVER_PROTOCOL']; 164 | $this->query = &$_GET; 165 | //$this->route = ''; 166 | $this->scheme = self::getRequestScheme(); 167 | $this->secure = $this->scheme == 'https'; 168 | $this->session = &$_SESSION; 169 | $this->xhr = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'; 170 | 171 | // Make data payloads available in $this->body for other HTTP verbs. 172 | if ( 173 | $this->method !== 'POST' 174 | && $this->get('content-type') == 'application/x-www-form-urlencoded' 175 | ) { 176 | parse_str(file_get_contents('php://input'), $_POST); 177 | } 178 | 179 | // Add support for multipart/form-data parsing for other HTTP verbs in PHP >=8.4. 180 | if ( 181 | $this->method !== 'POST' 182 | && $this->get('content-type') == 'multipart/form-data' 183 | && function_exists('request_parse_body') 184 | ) { 185 | [$_POST, $_FILES] = call_user_func('request_parse_body'); 186 | } 187 | } 188 | 189 | /** 190 | * Checks if the specified content type is acceptable, based on the request's Accept HTTP header field 191 | * 192 | * @param string $mimeType 193 | * @return bool 194 | */ 195 | public function accept(string $mimeType): bool 196 | { 197 | $header = $this->get('Accept'); 198 | if (!$header) 199 | { 200 | return false; 201 | } 202 | 203 | $mimeType = strtolower($mimeType); 204 | $arr = explode(',', $header); 205 | $arr = array_map('trim', $arr); 206 | $arr = array_map('strtolower', $arr); 207 | foreach ($arr as $item) 208 | { 209 | if (strpos($item, ';q=') !== false) 210 | { 211 | list($item,) = explode(';q=', $item); 212 | } 213 | 214 | // Accept: text/html 215 | // Value: text/html 216 | if ($item == $mimeType) 217 | { 218 | return true; 219 | } 220 | 221 | // Accept: */* 222 | if ($item == '*/*') 223 | { 224 | return true; 225 | } 226 | 227 | // Accept: text/* 228 | // Value: text/html 229 | if (strpos($item, '/*') !== false 230 | && substr($item, 0, strpos($item, '/')) == substr($mimeType, 0, strpos($mimeType, '/'))) 231 | { 232 | return true; 233 | } 234 | } 235 | 236 | return false; 237 | } 238 | 239 | /** 240 | * Checks if the specified encoding is acceptable, based on the request's Accept-Encoding HTTP header field 241 | * 242 | * @param string $encoding 243 | * @return bool 244 | */ 245 | public function acceptEncoding(string $encoding): bool 246 | { 247 | $header = $this->get('Accept-Encoding'); 248 | if (!$header) 249 | { 250 | return false; 251 | } 252 | 253 | $encoding = strtolower($encoding); 254 | $arr = explode(',', $header); 255 | $arr = array_map('trim', $arr); 256 | $arr = array_map('strtolower', $arr); 257 | foreach ($arr as $item) 258 | { 259 | $weight = 1; 260 | if (strpos($item, ';q=') !== false) 261 | { 262 | list($item, $weight) = explode(';q=', $item); 263 | } 264 | 265 | if ($weight == 0 && in_array($item, array('*', 'identity'))) 266 | { 267 | return false; 268 | } 269 | 270 | if ($item == '*') 271 | { 272 | return true; 273 | } 274 | 275 | if ($item == $encoding) 276 | { 277 | return true; 278 | } 279 | } 280 | 281 | return false; 282 | } 283 | 284 | /** 285 | * Checks if the specified language is acceptable, based on the request's Accept-Language HTTP header field 286 | * 287 | * @param string $language 288 | * @return bool 289 | */ 290 | public function acceptLanguage(string $language): bool 291 | { 292 | $header = $this->get('Accept-Language'); 293 | if (!$header) 294 | { 295 | return false; 296 | } 297 | 298 | $language = strtolower($language); 299 | $arr = explode(',', $header); 300 | $arr = array_map('trim', $arr); 301 | $arr = array_map('strtolower', $arr); 302 | foreach ($arr as $item) 303 | { 304 | if (strpos($item, ';q=') !== false) 305 | { 306 | list($item,) = explode(';q=', $item); 307 | } 308 | 309 | // Accept: en-US 310 | // Value: en-US 311 | if ($item == $language) 312 | { 313 | return true; 314 | } 315 | 316 | // Accept: * 317 | if ($item == '*') 318 | { 319 | return true; 320 | } 321 | } 322 | 323 | return false; 324 | } 325 | 326 | /** 327 | * Returns the specified HTTP request header field (case-insensitive match). 328 | * 329 | * @param string $name 330 | * @return string|null 331 | */ 332 | public static function get(string $name): ?string 333 | { 334 | $name = strtolower($name); 335 | 336 | $headers = function_exists('apache_request_headers') 337 | ? apache_request_headers() 338 | : self::getRequestHeaders(); 339 | 340 | foreach ($headers as $header => $value) 341 | { 342 | $header = strtolower($header); 343 | if ($header == $name) 344 | { 345 | return $value; 346 | } 347 | } 348 | 349 | return null; 350 | } 351 | 352 | /** 353 | * Alias of get() 354 | * 355 | * @param string $name 356 | * @return string|null 357 | */ 358 | public static function header(string $name): ?string 359 | { 360 | return self::get($name); 361 | } 362 | 363 | /** 364 | * Returns the matching content type if the incoming request's "Content-Type" HTTP header field matches the MIME 365 | * type specified by the type parameter. 366 | * 367 | * @param string $type 368 | * @return bool 369 | */ 370 | public static function is(string $type): bool 371 | { 372 | $contentType = self::get("content-type"); 373 | list($contentType,) = explode(";", $contentType); 374 | 375 | return $contentType == $type; 376 | } 377 | 378 | /** 379 | * Returns the request scheme, i.e. 'http' or 'https'. 380 | * 381 | * @return string 382 | */ 383 | protected static function getRequestScheme() : string 384 | { 385 | // Modern servers will have the HTTPS header set to 'on' or '1'. 386 | if ( 387 | isset($_SERVER['HTTPS']) 388 | && ( 389 | strtolower($_SERVER['HTTPS']) == 'on' 390 | || $_SERVER['HTTPS'] == 1 391 | ) 392 | ) { 393 | return 'https'; 394 | } 395 | // Some reverse proxies and load balencers will have the 396 | // HTTP_X_FORWARDED_PROTO header set to 'https'. 397 | else if ( 398 | isset($_SERVER['HTTP_X_FORWARDED_PROTO']) 399 | && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https' 400 | ) { 401 | 402 | return 'https'; 403 | } 404 | // Other reverse proxies and load balencers will have the 405 | // HTTP_FRONT_END_HTTPS headers set to 'on' or '1'. 406 | else if ( 407 | isset($_SERVER['HTTP_FRONT_END_HTTPS']) 408 | && ( 409 | strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) == 'on' 410 | || $_SERVER['HTTP_FRONT_END_HTTPS'] == 1 411 | ) 412 | ) { 413 | return 'https'; 414 | } 415 | // Apache server may have the REQUEST_SCHEME header available. 416 | else if ( 417 | isset($_SERVER['REQUEST_SCHEME']) 418 | && strtolower($_SERVER['REQUEST_SCHEME']) == 'https' 419 | ) { 420 | return 'https'; 421 | } 422 | // If all else fails, try the standard SSL server port '443'. 423 | else if ( 424 | isset($_SERVER['SERVER_PORT']) 425 | && intval($_SERVER['SERVER_PORT']) === 443 426 | ) { 427 | return 'https'; 428 | } 429 | return 'http'; 430 | } 431 | 432 | /** 433 | * Fetch all HTTP request headers 434 | * 435 | * @return array 436 | */ 437 | protected static function getRequestHeaders(): array 438 | { 439 | $arh = array(); 440 | foreach (headers_list() as $header) 441 | { 442 | $header = explode(":", $header); 443 | $arh[array_shift($header)] = trim(implode(":", $header)); 444 | } 445 | 446 | return $arh; 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /src/Response.php: -------------------------------------------------------------------------------- 1 | 'Continue', 45 | 101 => 'Switching Protocols', 46 | 102 => 'Processing', // RFC 2518, obsoleted by RFC 4918 47 | 103 => 'Early Hints', 48 | 200 => 'OK', 49 | 201 => 'Created', 50 | 202 => 'Accepted', 51 | 203 => 'Non-Authoritative Information', 52 | 204 => 'No Content', 53 | 205 => 'Reset Content', 54 | 206 => 'Partial Content', 55 | 207 => 'Multi-Status', // RFC 4918 56 | 208 => 'Already Reported', 57 | 226 => 'IM Used', 58 | 300 => 'Multiple Choices', // RFC 7231 59 | 301 => 'Moved Permanently', 60 | 302 => 'Found', 61 | 303 => 'See Other', 62 | 304 => 'Not Modified', 63 | 305 => 'Use Proxy', 64 | 307 => 'Temporary Redirect', 65 | 308 => 'Permanent Redirect', // RFC 7238 66 | 400 => 'Bad Request', 67 | 401 => 'Unauthorized', 68 | 402 => 'Payment Required', 69 | 403 => 'Forbidden', 70 | 404 => 'Not Found', 71 | 405 => 'Method Not Allowed', 72 | 406 => 'Not Acceptable', 73 | 407 => 'Proxy Authentication Required', 74 | 408 => 'Request Timeout', 75 | 409 => 'Conflict', 76 | 410 => 'Gone', 77 | 411 => 'Length Required', 78 | 412 => 'Precondition Failed', 79 | 413 => 'Payload Too Large', 80 | 414 => 'URI Too Long', 81 | 415 => 'Unsupported Media Type', 82 | 416 => 'Range Not Satisfiable', 83 | 417 => 'Expectation Failed', 84 | 418 => 'I\'m a Teapot', // RFC 7168 85 | 421 => 'Misdirected Request', 86 | 422 => 'Unprocessable Entity', // RFC 4918 87 | 423 => 'Locked', // RFC 4918 88 | 424 => 'Failed Dependency', // RFC 4918 89 | 425 => 'Unordered Collection', // RFC 4918 90 | 426 => 'Upgrade Required', // RFC 2817 91 | 428 => 'Precondition Required', // RFC 6585 92 | 429 => 'Too Many Requests', // RFC 6585 93 | 431 => 'Request Header Fields Too Large', // RFC 6585 94 | 451 => 'Unavailable For Legal Reasons', 95 | 500 => 'Internal Server Error', 96 | 501 => 'Not Implemented', 97 | 502 => 'Bad Gateway', 98 | 503 => 'Service Unavailable', 99 | 504 => 'Gateway Timeout', 100 | 505 => 'HTTP Version Not Supported', 101 | 506 => 'Variant Also Negotiates', // RFC 2295 102 | 507 => 'Insufficient Storage', // RFC 4918 103 | 508 => 'Loop Detected', 104 | 509 => 'Bandwidth Limit Exceeded', 105 | 510 => 'Not Extended', // RFC 2774 106 | 511 => 'Network Authentication Required' // RFC 6585 107 | ); 108 | 109 | /** 110 | * Response constructor 111 | * 112 | * @param Application $app 113 | */ 114 | public function __construct(Application $app) 115 | { 116 | $this->app = $app; 117 | $this->headersSent = headers_sent() ? 1 : 0; 118 | } 119 | 120 | /** 121 | * Appends the specified value to the HTTP response header field. If the header is not already set, it creates 122 | * the header with the specified value. The value parameter can be a string or an array. 123 | * 124 | * @param string $field 125 | * @param $value 126 | * @return Response 127 | */ 128 | public function append(string $field, $value): Response 129 | { 130 | $prev = $this->get($field); 131 | 132 | if ($prev) { 133 | if (is_array($prev)) { 134 | $value = array_merge($prev, array($value)); 135 | } elseif (is_array($value)) { 136 | array_unshift($value, $prev); 137 | } else { 138 | $value = array($prev, $value); 139 | } 140 | } 141 | 142 | return $this->set($field, $value); 143 | } 144 | 145 | /** 146 | * Sets the HTTP response Content-Disposition header field to "attachment" 147 | * 148 | * @param string|null $filename 149 | * @return Response 150 | */ 151 | public function attachment(string $filename=null): Response 152 | { 153 | $value = $filename 154 | ? 'attachment; filename="' . basename($filename) . '"' 155 | : 'attachment'; 156 | 157 | $this->set('Content-Disposition', $value); 158 | 159 | if ($filename) 160 | { 161 | $ext = pathinfo($filename, PATHINFO_EXTENSION); 162 | $this->type($ext, true); 163 | } 164 | 165 | return $this; 166 | } 167 | 168 | /** 169 | * Clears the cookie specified by name. 170 | * 171 | * @param string $name 172 | * @param array $options 173 | * @return Response 174 | */ 175 | public function clearCookie(string $name, array $options=array()): Response 176 | { 177 | $options = array_merge(array( 178 | 'expire' => time() - 3600, 179 | 'path' => '/' 180 | ), $options); 181 | 182 | return $this->cookie($name, "", $options); 183 | } 184 | 185 | /** 186 | * Alias of type() 187 | * 188 | * @param string $type 189 | * @param bool $fallback 190 | * @return Response 191 | */ 192 | public function contentType(string $type, bool $fallback=false): Response 193 | { 194 | return $this->type($type, $fallback); 195 | } 196 | 197 | /** 198 | * Sets cookie name to value. The value parameter may be a string or array converted to JSON. 199 | * 200 | * @param string $name 201 | * @param string $value 202 | * @param array $options 203 | * @return Response 204 | */ 205 | public function cookie(string $name, string $value, array $options=array()): Response 206 | { 207 | $expire = isset($options['expire']) ? $options['expire'] : 0; 208 | $path = isset($options['path']) ? $options['path'] : ""; 209 | $domain = isset($options['domain']) ? $options['domain'] : ""; 210 | $secure = isset($options['secure']) ? $options['secure'] : false; 211 | $httpOnly = isset($options['httpOnly']) ? $options['httpOnly'] : false; 212 | if (isset($options['sameSite'])) 213 | { 214 | if (is_string($options['sameSite']) && in_array(strtolower($options['sameSite']), array("lax", "strict"))) { 215 | $sameSite = strtolower($options['sameSite']); 216 | } elseif (is_bool($options['sameSite'])) { 217 | if (!$options['sameSite']) { 218 | $sameSite = "lax"; 219 | } elseif ($options['sameSite']) { 220 | $sameSite = "strict"; 221 | } 222 | } 223 | if (isset($sameSite)) { 224 | $path .= "; samesite=" . $sameSite; 225 | } 226 | } 227 | 228 | setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); 229 | 230 | return $this; 231 | } 232 | 233 | /** 234 | * Transfers the file at path as an "attachment". Typically, browsers will prompt the user for download. 235 | * 236 | * @param string $path 237 | * @param string|null $filename 238 | */ 239 | public function download(string $path, string $filename=null) 240 | { 241 | $name = $filename ? $filename : basename($path); 242 | 243 | $this 244 | ->set('Pragma', 'public') 245 | ->set('Expires', 0) 246 | ->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') 247 | ->set('Cache-Control', 'private') 248 | ->set('Content-Transfer-Encoding', 'binary'); 249 | 250 | $this->attachment($name); 251 | 252 | $length = @filesize($path); 253 | if ($length > 0) 254 | { 255 | $this->set('Content-Length', $length); 256 | } 257 | 258 | $this->sendHeaders(); 259 | 260 | $chunkSize = 1024 * 1024; 261 | $handle = fopen($path, 'rb'); 262 | while (!feof($handle)) 263 | { 264 | $buffer = fread($handle, $chunkSize); 265 | echo $buffer; 266 | ob_flush(); 267 | flush(); 268 | } 269 | fclose($handle); 270 | } 271 | 272 | /** 273 | * Ends the response process. Use to quickly end the response without any data. 274 | * 275 | * @param string|null $data 276 | */ 277 | public function end(string $data = null): void 278 | { 279 | if ($data) 280 | { 281 | echo $data; 282 | } 283 | exit; 284 | } 285 | 286 | /** 287 | * Returns the HTTP response header specified by field. The match is case-insensitive. 288 | * 289 | * @param string $field 290 | * @return string|null 291 | */ 292 | public function get(string $field): ?string 293 | { 294 | $field = strtolower($field); 295 | foreach (headers_list() as $header) { 296 | list ($name, $value) = explode(":", $header, 2); 297 | if (strtolower($name) == $field) { 298 | return trim($value); 299 | } 300 | } 301 | 302 | return null; 303 | } 304 | 305 | /** 306 | * Return an array with response headers. 307 | * 308 | * @return array 309 | */ 310 | public function getHeaders(): array 311 | { 312 | return $this->headers; 313 | } 314 | 315 | /** 316 | * Alias of set() 317 | * 318 | * @param string $field 319 | * @param $value 320 | * @return Response 321 | */ 322 | public function header(string $field, $value): Response 323 | { 324 | return $this->set($field, $value); 325 | } 326 | 327 | /** 328 | * Sends a JSON response. This method sends a response (with the correct content-type) that is the parameter 329 | * converted to a JSON string using json_encode(). 330 | * 331 | * @param mixed $body 332 | * @param int $flags 333 | * @return Response 334 | */ 335 | public function json($body, int $flags = 0): Response 336 | { 337 | $body = json_encode($body, $flags); 338 | 339 | if ($this->get("Content-Type") != "application/json") { 340 | $this->type("json"); 341 | } 342 | 343 | return $this->send($body); 344 | } 345 | 346 | /** 347 | * Sets the response Location HTTP header to the specified path parameter. 348 | * 349 | * @param string $path 350 | * @return Response 351 | */ 352 | public function location(string $path): Response 353 | { 354 | return $this->set("Location", $path); 355 | } 356 | 357 | /** 358 | * Detect mime type 359 | * 360 | * @param string $value 361 | * @return string 362 | */ 363 | protected static function mimeLookup(string $value) 364 | { 365 | $value = strtolower($value); 366 | 367 | switch ($value) { 368 | case "html": 369 | $mimeType = "text/html"; 370 | break; 371 | case "js": 372 | case "javascript": 373 | $mimeType = "application/javascript"; 374 | break; 375 | case "json": 376 | $mimeType = "application/json"; 377 | break; 378 | case "css": 379 | $mimeType = "text/css"; 380 | break; 381 | case "text": 382 | $mimeType = "text/plain"; 383 | break; 384 | case "png": 385 | case "apng": 386 | case "gif": 387 | case "webp": 388 | case "avif": 389 | $mimeType = "image/$value"; 390 | break; 391 | case "jpg": 392 | case "jpeg": 393 | $mimeType = "image/jpeg"; 394 | break; 395 | case "svg": 396 | $mimeType = "image/svg+xml"; 397 | break; 398 | case "pdf": 399 | $mimeType = "application/pdf"; 400 | break; 401 | default: 402 | $mimeType = ""; 403 | } 404 | 405 | return $mimeType; 406 | } 407 | 408 | /** 409 | * Redirects to the URL derived from the specified path, with specified status, a positive integer that 410 | * corresponds to an HTTP status code. 411 | * 412 | * @param string $path 413 | * @param int $status 414 | */ 415 | public function redirect(string $path, int $status = 302): void 416 | { 417 | $this->location($path); 418 | 419 | $this->sendHeaders(); 420 | $this->status($status); 421 | $this->end(); 422 | } 423 | 424 | /** 425 | * Renders a view and sends the rendered HTML string to the client. 426 | * 427 | * @param string $view 428 | * @param array $locals 429 | */ 430 | public function render(string $view, array $locals = array()): void 431 | { 432 | $this->sendHeaders(); 433 | $this->status(200); 434 | $this->app->render($view, $locals); 435 | } 436 | 437 | /** 438 | * Sends the HTTP response. 439 | * 440 | * @param mixed $body 441 | * @return Response 442 | */ 443 | public function send($body): Response 444 | { 445 | $this->sendHeaders(); 446 | 447 | if ($body) { 448 | switch (true) { 449 | case is_string($body): 450 | if (!$this->get('Content-Type')) { 451 | $this->type('html'); 452 | } 453 | break; 454 | case is_array($body): 455 | case is_object($body): 456 | case is_numeric($body): 457 | case is_bool($body): 458 | if (!is_null($body)) { 459 | return $this->json($body); 460 | } 461 | break; 462 | } 463 | } 464 | 465 | $this->end($body); 466 | 467 | return $this; 468 | } 469 | 470 | /** 471 | * Send a raw HTTP header 472 | * 473 | * @param string $header 474 | * @param bool $replace 475 | * @param int $statusCode 476 | * @return Response 477 | */ 478 | protected function sendHeader(string $header, bool $replace=true, int $statusCode=0): Response 479 | { 480 | if ($statusCode) 481 | { 482 | header($header, $replace, $statusCode); 483 | } else { 484 | header($header, $replace); 485 | } 486 | 487 | return $this; 488 | } 489 | 490 | /** 491 | * @param string $name 492 | * @param string|bool $value 493 | * @return Response 494 | */ 495 | protected function proceedHeader(string $name, $value): Response 496 | { 497 | if (is_string($value) && strlen($value) > 0) 498 | { 499 | $this->sendHeader("$name: $value"); 500 | } elseif ($value === false) { 501 | $this->removeHeader($name); 502 | } 503 | 504 | return $this; 505 | } 506 | 507 | /** 508 | * Remove previously set headers 509 | * 510 | * @param string $name 511 | * @return Response 512 | */ 513 | protected function removeHeader(string $name): Response 514 | { 515 | header_remove($name); 516 | 517 | return $this; 518 | } 519 | 520 | /** 521 | * @return Response 522 | */ 523 | protected function sendHeaders(): Response 524 | { 525 | foreach ($this->headers as $name => $value) 526 | { 527 | if (is_array($value)) 528 | { 529 | foreach ($value as $val) 530 | { 531 | $this->proceedHeader($name, $val); 532 | } 533 | } else { 534 | $this->proceedHeader($name, $value); 535 | } 536 | } 537 | 538 | return $this; 539 | } 540 | 541 | /** 542 | * Sets the response HTTP status code to statusCode and send its string representation as the response body. 543 | * 544 | * @param int $code 545 | * @return Response 546 | */ 547 | public function sendStatus(int $code): Response 548 | { 549 | return $this->type("text")->status($code)->send($this->statusMessage); 550 | } 551 | 552 | /** 553 | * Sets the response's HTTP header field to value. To set multiple fields at once, pass an array as the parameter. 554 | * 555 | * @param array|string $field 556 | * @param mixed|null $value 557 | * @return Response 558 | */ 559 | public function set($field, $value=null): Response 560 | { 561 | if (is_array($field)) { 562 | foreach ($field as $key => $val) { 563 | $this->headers[$key] = $val; 564 | } 565 | } else { 566 | $this->headers[$field] = $value; 567 | } 568 | 569 | return $this; 570 | } 571 | 572 | /** 573 | * Sets the HTTP status for the response. 574 | * 575 | * @param int $code 576 | * @return Response 577 | */ 578 | public function status(int $code): Response 579 | { 580 | $this->statusCode = $code; 581 | 582 | $this->statusMessage = isset(self::STATUS_CODES[$code]) ? self::STATUS_CODES[$code] : "$code"; 583 | 584 | $this->sendHeader("HTTP/1.1 $this->statusCode $this->statusMessage"); 585 | 586 | return $this; 587 | } 588 | 589 | /** 590 | * Sets the Content-Type HTTP header to the MIME type as determined by mimeLookup() for the specified type. 591 | * 592 | * @param string $value 593 | * @param bool $fallback 594 | * @return Response 595 | */ 596 | public function type(string $value, bool $fallback=false): Response 597 | { 598 | $contentType = self::mimeLookup($value); 599 | if ($contentType) 600 | { 601 | $this->set("Content-Type", $contentType); 602 | } elseif ($fallback) { 603 | $this->set("Content-Type", "application/octet-stream"); 604 | } 605 | 606 | return $this; 607 | } 608 | 609 | /** 610 | * Adds the field to the Vary response header, if it is not there already. 611 | * 612 | * @param string $value 613 | * @return Response 614 | */ 615 | public function vary(string $value): Response 616 | { 617 | return $this->set("Vary", $value); 618 | } 619 | } -------------------------------------------------------------------------------- /src/Route.php: -------------------------------------------------------------------------------- 1 | setPath($path); 56 | } 57 | 58 | /** 59 | * Gets a callback 60 | * 61 | * @return array|callable|string 62 | */ 63 | public function getCallback() 64 | { 65 | return $this->callback; 66 | } 67 | 68 | /** 69 | * Sets a callback 70 | * 71 | * @param array|string|callable $callback 72 | * @return Route 73 | */ 74 | public function setCallback($callback): Route 75 | { 76 | $this->callback = $callback; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * Gets a path 83 | * 84 | * @return string 85 | */ 86 | public function getPath(): string 87 | { 88 | return $this->path; 89 | } 90 | 91 | /** 92 | * Sets a path 93 | * 94 | * @param string $path 95 | * @return Route 96 | */ 97 | public function setPath(string $path): Route 98 | { 99 | $this->path = $path; 100 | 101 | return $this; 102 | } 103 | 104 | /** 105 | * Gets a method 106 | * 107 | * @return string 108 | */ 109 | public function getMethod(): string 110 | { 111 | return $this->method; 112 | } 113 | 114 | /** 115 | * Sets a method 116 | * 117 | * @param string $method 118 | * @return Route 119 | */ 120 | public function setMethod(string $method): Route 121 | { 122 | $this->method = $method; 123 | 124 | return $this; 125 | } 126 | 127 | /** 128 | * Gets a name 129 | * 130 | * @return string 131 | */ 132 | public function getName(): string 133 | { 134 | return $this->name; 135 | } 136 | 137 | /** 138 | * Sets a name 139 | * 140 | * @param string $name 141 | * @return Route 142 | */ 143 | public function setName(string $name): Route 144 | { 145 | $this->name = $name; 146 | 147 | return $this; 148 | } 149 | 150 | /** 151 | * Gets an application instance 152 | * 153 | * @return Application 154 | */ 155 | public function getApplication(): Application 156 | { 157 | return $this->app; 158 | } 159 | 160 | /** 161 | * Sets an application 162 | * 163 | * @param Application $app 164 | * @return Route 165 | */ 166 | public function setApplication(Application $app): Route 167 | { 168 | $this->app = $app; 169 | 170 | return $this; 171 | } 172 | 173 | /** 174 | * @param string $name Accepts: delete, get, head, options, patch, post, put, all, use 175 | * @param array $arguments 176 | * @return Route 177 | */ 178 | public function __call(string $name, array $arguments): Route 179 | { 180 | $methods = array_merge(self::METHODS, array('all', 'use')); 181 | if (!in_array($name, $methods)) 182 | { 183 | trigger_error("The '$name' method is not defined", E_USER_WARNING); 184 | } 185 | 186 | $this->setMethod($name); 187 | $this->setCallback($arguments); 188 | 189 | return $this; 190 | } 191 | 192 | /** 193 | * @param callable|string $argument 194 | * @param bool $use 195 | * @return Route 196 | */ 197 | public function dispatch($argument, bool $use = false): Route 198 | { 199 | if (is_callable($argument)) 200 | { 201 | $argument($this->app->getRequest(), $this->app->getResponse()); 202 | 203 | } elseif (is_string($argument)) { 204 | 205 | list($className, $method) = explode("@", $argument); 206 | 207 | try { 208 | $reflectionMethod = new \ReflectionMethod($className, $method); 209 | if ($use) 210 | { 211 | echo $reflectionMethod->invoke(new $className, $this->app->getRequest(), $this->app->getResponse(), function () { 212 | //echo '
';
213 | 	                    //print_r($this->app->getResponse()->app->router);
214 | 	                });
215 | 	            } else {
216 | 	                echo $reflectionMethod->invoke(new $className, $this->app->getRequest(), $this->app->getResponse());
217 | 	            }
218 | 	        } catch (\ReflectionException $e) {
219 | 	            printf("%s in %s on line %s", $e->getMessage(), $e->getFile(), $e->getLine());
220 | 	        } catch (\Exception $e) {
221 | 	            printf("%s in %s on line %s", $e->getMessage(), $e->getFile(), $e->getLine());
222 |             }
223 |         }
224 | 
225 |         return $this;
226 |     }
227 | }


--------------------------------------------------------------------------------
/src/Router.php:
--------------------------------------------------------------------------------
  1 | app = $app !== null && $app instanceof Application
 31 |             ? $app
 32 |             : new Application();
 33 |     }
 34 | 
 35 |     /**
 36 |      * @return Route[]
 37 |      */
 38 |     public function getRoutes(): array
 39 |     {
 40 |         return $this->routes;
 41 |     }
 42 | 
 43 |     /**
 44 |      * Add custom parameters to use in a route path.
 45 |      *
 46 |      * @param string $name
 47 |      * @param string|null $regex
 48 |      * @return Router
 49 |      */
 50 |     public function param(string $name, string $regex = null): Router
 51 |     {
 52 |         $this->app->param($name, $regex);
 53 | 
 54 |         return $this;
 55 |     }
 56 | 
 57 |     /**
 58 |      * Returns an instance of a single route which you can then use to handle HTTP verbs with optional middleware.
 59 |      * Use $router->route() to avoid duplicate route naming and thus typing errors.
 60 |      *
 61 |      * @param string $path
 62 |      * @return Route
 63 |      */
 64 |     public function route(string $path): Route
 65 |     {
 66 |         $route = new Route($path);
 67 |         $route->setApplication($this->app);
 68 | 
 69 |         $this->routes[] = $route;
 70 | 
 71 |         return $route;
 72 |     }
 73 | 
 74 |     /**
 75 |      * Run the application
 76 |      */
 77 |     public function run(): void
 78 |     {
 79 |         $this->app->router = $this;
 80 |         $this->app->run();
 81 |     }
 82 | 
 83 |     /**
 84 |      * Routes HTTP (verb) requests to the specified path with the specified callback function.
 85 |      *
 86 |      * @param string $name Accepts: delete, get, head, options, patch, post, put, all
 87 |      * @param array $arguments
 88 |      * @return Router
 89 |      */
 90 |     public function __call(string $name, array $arguments): Router
 91 |     {
 92 |         $methods = array_merge(Route::METHODS, array('all'));
 93 |         if (!in_array($name, $methods))
 94 |         {
 95 |             trigger_error("The '$name' method is not defined", E_USER_WARNING);
 96 |         }
 97 | 
 98 |         $route = $this->route($arguments[0]);
 99 |         $route->setMethod($name);
100 |         $route->setCallback(array_slice($arguments, 1));
101 | 
102 |         //call_user_func_array(array($route, $name), array_slice($arguments, 1));
103 | 
104 |         return $this;
105 |     }
106 | }


--------------------------------------------------------------------------------
/src/autoload.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, Application::class);
22 |         }
23 |     }
24 | }


--------------------------------------------------------------------------------
/tests/RequestTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, Request::class);
34 |         }
35 |     }
36 | }


--------------------------------------------------------------------------------
/tests/ResponseTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, Response::class);
23 |         }
24 |     }
25 | }


--------------------------------------------------------------------------------
/tests/RouteTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, Route::class);
23 |         }
24 |     }
25 | 
26 |     public function testPath()
27 |     {
28 |         $route = new Route('/');
29 | 
30 |         $route->setPath('/profile');
31 |         $this->assertSame('/profile', $route->getPath());
32 |     }
33 | 
34 |     public function testMethod()
35 |     {
36 |         $route = new Route('/');
37 | 
38 |         $route->setMethod('POST');
39 |         $this->assertSame('POST', $route->getMethod());
40 |     }
41 | 
42 |     public function testApplication()
43 |     {
44 |         $route = new Route('/');
45 | 
46 |         $app = new Application();
47 |         $route->setApplication($app);
48 |         $this->assertSame($app, $route->getApplication());
49 |     }
50 | 
51 |     public function testName()
52 |     {
53 |         $route = new Route('/');
54 | 
55 |         $route->setName('test');
56 |         $this->assertSame('test', $route->getName());
57 |     }
58 | 
59 |     public function testCallback()
60 |     {
61 |         $route = new Route('/');
62 | 
63 |         $route->setCallback('test');
64 |         $this->assertSame('test', $route->getCallback());
65 |     }
66 | 
67 |     public function testDispatch()
68 |     {
69 |         $route = new Route('/');
70 |         $route->setApplication(new Application());
71 | 
72 |         $fn = function($req, $res) {
73 |             echo 'test';
74 |         };
75 | 
76 |         ob_start();
77 |         $this->assertInstanceOf(Route::class, $route->dispatch($fn));
78 |         $content = ob_get_contents();
79 |         ob_end_clean();
80 |         $this->assertSame('test', $content);
81 |     }
82 | }


--------------------------------------------------------------------------------
/tests/RouterTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, Router::class);
20 |         }
21 |     }
22 | 
23 |     public function testRoute()
24 |     {
25 |         $router = new Router();
26 |         $route = $router->route('/');
27 | 
28 |         $this->assertInstanceOf(Route::class, $route);
29 |     }
30 | }


--------------------------------------------------------------------------------