├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── blueprintdocs.php ├── example.apib ├── package-lock.json ├── package.json ├── public ├── css │ ├── app.css │ ├── blueprintdocs.css │ └── vendor.css ├── fonts │ └── vendor │ │ └── bootstrap-sass │ │ └── bootstrap │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 ├── js │ ├── app.js │ └── blueprintdocs.js └── mix-manifest.json ├── resources ├── assets │ ├── js │ │ ├── app.js │ │ └── blueprintdocs.js │ └── sass │ │ ├── _highlight.scss │ │ ├── _variables.scss │ │ ├── app.scss │ │ └── blueprintdocs.scss └── views │ ├── action.blade.php │ ├── index.blade.php │ ├── navigation.blade.php │ ├── parameters.blade.php │ ├── requestresponsebody.blade.php │ ├── resource.blade.php │ └── resource_group.blade.php ├── src ├── BlueprintDocs.php ├── BlueprintDocsController.php ├── BlueprintDocsServiceProvider.php ├── DescriptionParser.php ├── Elements │ ├── Action.php │ ├── Api.php │ ├── Asset.php │ ├── Base.php │ ├── HrefVariable.php │ ├── HttpRequest.php │ ├── HttpResponse.php │ ├── Mapping.php │ ├── Resource.php │ └── ResourceGroup.php └── routes.php └── webpack.mix.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /vendor 3 | .DS_Store 4 | .idea 5 | composer.lock -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When wanting to contribute to this repository, please first 4 | 5 | * discuss the change you wish to make via issue, 6 | email, or any other method with the owners of this repository, 7 | * follow this excellent tutorial on [How To Create a Pull Request on GitHub](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github) and 8 | * acknowledge our Code of Conduct in all your interactions with the project. 9 | 10 | ## Code of Conduct 11 | 12 | ### Our Pledge 13 | 14 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 15 | 16 | ### Our Standards 17 | 18 | Examples of behavior that contributes to creating a positive environment include: 19 | 20 | * Using welcoming and inclusive language 21 | * Being respectful of differing viewpoints and experiences 22 | * Gracefully accepting constructive criticism 23 | * Focusing on what is best for the community 24 | * Showing empathy towards other community members 25 | 26 | Examples of unacceptable behavior by participants include: 27 | 28 | * The use of sexualized language or imagery and unwelcome sexual attention or 29 | advances 30 | * Trolling, insulting/derogatory comments, and personal or political attacks 31 | * Public or private harassment 32 | * Publishing others' private information, such as a physical or electronic 33 | address, without explicit permission 34 | * Other conduct which could reasonably be considered inappropriate in a 35 | professional setting 36 | 37 | ### Our Responsibilities 38 | 39 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 40 | 41 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 42 | 43 | ### Scope 44 | 45 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 46 | 47 | ### Enforcement 48 | 49 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 50 | 51 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 52 | 53 | ### Attribution 54 | 55 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 56 | 57 | [homepage]: http://contributor-covenant.org 58 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Michael Schmidt-Voigt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Blueprint Docs](https://i.imgur.com/m6Abbmc.png) 2 | 3 |

4 | Latest Stable Version 5 | Total Downloads 6 | Latest Unstable Version 7 | License 8 | Twitter 9 |

10 | 11 | # API Blueprint Renderer for Laravel 12 | 13 | This Laravel package *Blueprint Docs* renders your [API Blueprint](http://apiblueprint.org/). It comes with a standard theme that you can customize via Blade templates. Install the package and find your rendered documentation at route `/api-documentation`. 14 | 15 | **Example output**: If used with [API Blueprint boilerplate](https://github.com/jsynowiec/api-blueprint-boilerplate), this would be [*Blueprint Docs'* output](https://m165437.github.io/laravel-blueprint-docs). 16 | 17 | API Blueprint is a Markdown-based document format that lets you write API descriptions and documentation in a simple and straightforward way. Currently supported is [API Blueprint format 1A](https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md). 18 | 19 | ## Requirements 20 | 21 | * Laravel 5.4 or greater 22 | * Drafter (the official C++ API Blueprint parser) [command line tool](https://github.com/apiaryio/drafter#drafter-command-line-tool) 23 | * A valid API Blueprint `blueprint.apib` in the root directory of your Laravel project (example available) 24 | 25 | **Drafter is not included** and must be installed beforehand. Use the [Drafter Installer](https://github.com/hendrikmaus/drafter-installer) composer package to "install drafter in your php project with ease". Head over there and install it now. 26 | 27 | ## Installation 28 | 29 | Install the package via composer: 30 | 31 | ``` bash 32 | composer require m165437/laravel-blueprint-docs 33 | ``` 34 | 35 | Next, register its service provider (Laravel >= 5.5 does this automatically via [Package Discovery](https://laravel.com/docs/5.5/packages#package-discovery)): 36 | 37 | ```php 38 | // config/app.php 39 | 'providers' => [ 40 | ... 41 | M165437\BlueprintDocs\BlueprintDocsServiceProvider::class, 42 | ]; 43 | ``` 44 | 45 | Optionally, publish the example [API Blueprint boilerplate](https://github.com/jsynowiec/api-blueprint-boilerplate) file `blueprint.apib` to the root directory of your Laravel project: 46 | 47 | ```bash 48 | php artisan vendor:publish --provider="M165437\BlueprintDocs\BlueprintDocsServiceProvider" --tag="example" 49 | ``` 50 | 51 | Finally, publish its assets to `public/vendor/blueprintdocs`: 52 | 53 | ```bash 54 | php artisan vendor:publish --provider="M165437\BlueprintDocs\BlueprintDocsServiceProvider" --tag="public" 55 | ``` 56 | 57 | Find your documentation at route `/api-documentation`. 58 | 59 | ## Update 60 | 61 | When you update this package, you might need to republish its assets (note the added parameter `--force`): 62 | 63 | ```bash 64 | php artisan vendor:publish --provider="M165437\BlueprintDocs\BlueprintDocsServiceProvider" --tag="public" --force 65 | ``` 66 | 67 | ## Configuration 68 | 69 | To adjust Blueprint Docs' configuration, publish its config file to `config/blueprintdocs.php`: 70 | 71 | ``` bash 72 | php artisan vendor:publish --provider="M165437\BlueprintDocs\BlueprintDocsServiceProvider" --tag="config" 73 | ``` 74 | 75 | The default contents of the configuration file look like this: 76 | 77 | ```php 78 | return [ 79 | 80 | /* 81 | |-------------------------------------------------------------------------- 82 | | Blueprint Docs 83 | |-------------------------------------------------------------------------- 84 | | 85 | | Find your rendered docs at the given route or set route to false if you 86 | | want to use your own route and controller. Provide a fully qualified 87 | | path to your API blueprint as well as to the required Drafter CLI. 88 | | 89 | */ 90 | 91 | 'route' => 'api-documentation', 92 | 93 | 'condense_navigation' => false, 94 | 95 | 'blueprint_file' => base_path('blueprint.apib'), 96 | 97 | 'drafter' => base_path('vendor/bin/drafter') 98 | 99 | ]; 100 | ``` 101 | 102 | If you want to use Blueprint Docs with your own route and controller, set `'route' => false` and have a look at `vendor/m165437/laravel-blueprint-docs/src/BlueprintDocsController.php` to get an idea on how to set it up. 103 | 104 | ## Theming 105 | 106 | To customize the default theme, publish its views to `views/vendor/blueprintdocs`: 107 | 108 | ``` bash 109 | php artisan vendor:publish --provider="M165437\BlueprintDocs\BlueprintDocsServiceProvider" --tag="views" 110 | ``` 111 | 112 | ## Contributing 113 | 114 | Thank you for considering contributing to this package! Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 115 | 116 | ## Credits 117 | 118 | This package relies heavily on work done by [Hendrik Maus](https://github.com/hendrikmaus), namely his [Drafter PHP Wrapper](https://github.com/hendrikmaus/drafter-php) and [Reynaldo](https://github.com/hendrikmaus/reynaldo), it's inspired by [Aglio](https://github.com/danielgtaylor/aglio), an API Blueprint renderer written in Node.js, and provides the [API Blueprint boilerplate](https://github.com/jsynowiec/api-blueprint-boilerplate) as an example. The header is the modified part of a [graphic created by Iconicbestiary](http://www.freepik.com/free-vector/hands-signing-house-or-apartment-contract_1311557.htm), via [Freepik.com](http://www.freepik.com). 119 | 120 | ## License 121 | 122 | Blueprint Docs is licensed under the MIT License (MIT). Please see the [LICENSE](LICENSE.md) file for more information. 123 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "m165437/laravel-blueprint-docs", 3 | "description": "API Blueprint Renderer for Laravel", 4 | "keywords": [ 5 | "api", 6 | "blueprint", 7 | "renderer", 8 | "laravel", 9 | "docs" 10 | ], 11 | "license": "MIT", 12 | "homepage": "https://github.com/M165437/laravel-blueprint-docs", 13 | "authors": [ 14 | { 15 | "name": "Michael Schmidt-Voigt", 16 | "email": "michael@schmidt-voigt.de", 17 | "homepage": "https://github.com/M165437", 18 | "role": "Developer" 19 | } 20 | ], 21 | "require": { 22 | "php": ">=5.6.4", 23 | "laravel/framework": ">=5.4|~7.0", 24 | "hmaus/drafter-php": "^6.1.1", 25 | "hmaus/reynaldo": "^0.1.5", 26 | "erusev/parsedown": "^1.7.0", 27 | "erusev/parsedown-extra": "^0.7.1" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "M165437\\BlueprintDocs\\": "src/" 32 | } 33 | }, 34 | "extra": { 35 | "laravel": { 36 | "providers": [ 37 | "M165437\\BlueprintDocs\\BlueprintDocsServiceProvider" 38 | ] 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /config/blueprintdocs.php: -------------------------------------------------------------------------------- 1 | 'api-documentation', 17 | 18 | 'condense_navigation' => false, 19 | 20 | 'blueprint_file' => base_path('blueprint.apib'), 21 | 22 | 'drafter' => base_path('vendor/bin/drafter') 23 | 24 | ]; -------------------------------------------------------------------------------- /example.apib: -------------------------------------------------------------------------------- 1 | FORMAT: 1A 2 | HOST: https://api.example.com 3 | 4 | # Blueprint Docs 5 | 6 | *This API blueprint is subject to change due to technology restrictions, performance optimizations or changing requirements.* 7 | 8 | ## Authentication 9 | 10 | + This API uses [JWT](http://jwt.io/) for authentication, 11 | + Every token MUST be refreshed before its expiration time, 12 | + Token MUST be provided in `Authorization` header, 13 | + Toke MUST be provided for each request that requires authentication, 14 | + This API issues a **long-lived access tokens** for consumers. A long-lived JWT generally SHOULD lasts about **30 days**. If no requests are made, the token MUST expire and the user MUST go through the login flow again to get a new one. 15 | 16 | > A custom scheme like "JWT" seems to be more appropriate than coercing the OAuth2 Bearer scheme. 17 | 18 | ### Example Header 19 | ``` 20 | Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhNnZoQW8zRkc3dDEiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE0NzA1OTg5NzIsImV4cCI6MTQ3MDY4NTM3Mn0.ltA9zZmJKszBJuuV7pTWtY7LzLXrRUfebJDhy_jGMeM 21 | ``` 22 | 23 | ### Claims 24 | + `exp` - The exp ( *expiration time* ) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. 25 | + `iat` - The iat ( *issued at* ) claim identifies the time at which the JWT was issued. 26 | + `sub` - The aud ( *audience* ) claim identifies the subject of this token (for e.g. a user id). 27 | + `iss` - The iss ( *issuer* ) claim identifies the principal that issued the JWT. 28 | 29 | ## Consumer Identification 30 | 31 | This API uses `User-Agent` and `Application-Id` headers to identify API consumer. `Application-Id` MUST contain an UUID that uniquely identifies a particular consumer installation. 32 | 33 | ### Example Headers 34 | ``` 35 | User-Agent: Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36 36 | Application-Id: 6454d937-0a18-44a8-b482-bb48684f1ed4 37 | ``` 38 | 39 | ## Filtering, Ordering, Pagination & Searching 40 | 41 | ### Filtering 42 | 43 | This API can filter returned collections based on provided query parameters. Virtually any model field can be used as a filter. 44 | 45 | For example, when requesting a list of movies from the /movies endpoint, you may want to limit these to only those of drama genre. This could be accomplished with a request like `GET /movies?genre=drama`. Here, genre is a query parameter that implements a filter. 46 | 47 | ### Ordering 48 | 49 | This API can sort returned collections. A generic query parameter `sort` is used to describe sorting rules. This parameter can take a list of comma separated field, each with a possible unary negative to imply descending sort order. 50 | 51 | E.g. `GET /movies?sort=-rating` - Retrieves a list of movies in descending order of user rating. 52 | 53 | By default all resources are ordered by their creation time, from newest to oldest. 54 | 55 | ### Pagination 56 | 57 | This API uses the [Link header - RFC 5988](http://tools.ietf.org/html/rfc5988#page-6) to include pagination details. 58 | 59 | An example of a Link header is described in [GitHub documentation](https://developer.github.com/guides/traversing-with-pagination/). 60 | 61 | This API returns total count of paged resources in `Total-Count` HTTP header. 62 | 63 | ### Searching 64 | 65 | This API uses a generic parameter `search` to expose a full text search mechanism. 66 | 67 | ## HTTP Methods 68 | 69 | This API uses HTTP verbs (methods) as following: 70 | 71 | + `GET` - *Read* - used to **read** (or retrieve) a representation of a resource, 72 | + `POST` - *Create* - used to **create** new resources. In particular, it's used to create subordinate resources. 73 | + `PUT` - *Update/Replace* - used for **update** capabilities, PUT-ing to a known resource URI with the request body containing the newly-updated representation of the original resource. On successful request, replaces identified resource with the request body. 74 | + `PATCH` - *Update/Modify* - used for **modify** capabilities. The PATCH request only needs to contain the changes to the resource, not the complete resource. 75 | + `DELETE` - *Delete* - used to **delete** a resource identified by a URI. 76 | 77 | ## Localization 78 | 79 | This API uses `Accept-Language` header to identify the locale. 80 | 81 | `Accept-Language: en` 82 | 83 | This header SHOULD be present in every request. If not, API MUST use the **english** language/locale. 84 | 85 | ## Media Type 86 | 87 | Where applicable this API MUST use the JSON media-type. Requests with a message-body are using plain JSON to set or update resource states. 88 | 89 | `Content-type: application/json` and `Accept: application/json` headers SHOULD be set on all requests if not stated otherwise. 90 | 91 | ## Notational Conventions 92 | 93 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://www.ietf.org/rfc/rfc2119). 94 | 95 | ## Representation of Date and Time 96 | 97 | All exchange of date and time-related data MUST be done according to ISO 8601 standard and stored in UTC. 98 | 99 | When returning date and time-related data `YYYY-MM-DDThh:mm:ss.SSSZ` format MUST be used. 100 | 101 | ## Resource IDs 102 | 103 | This API uses short non-sequential url-friendly unique ids. Every resource id MUST consists of 9 url-friendly characters: `A-Z`, `a-z`, `0-9`, `_` and `-`. 104 | 105 | ### Example 106 | `a6vhAo3FG7t1` 107 | 108 | ## Status Codes and Errors 109 | 110 | This API uses HTTP status codes to communicate with the API consumer. 111 | 112 | + `200 OK` - Response to a successful GET, PUT, PATCH or DELETE. 113 | + `201 Created` - Response to a POST that results in a creation. 114 | + `204 No Content` - Response to a successful request that won't be returning a body (like a DELETE request). 115 | + `400 Bad Request` - Malformed request; form validation errors. 116 | + `401 Unauthorized` - When no or invalid authentication details are provided. 117 | + `403 Forbidden` - When authentication succeeded but authenticated user doesn't have access to the resource. 118 | + `404 Not Found` - When a non-existent resource is requested. 119 | + `405 Method Not Allowed` - Method not allowed. 120 | + `406 Not Acceptable` - Could not satisfy the request Accept header. 121 | + `415 Unsupported Media Type` - Unsupported media type in request. 122 | 123 | ### Error response 124 | 125 | This API returns both, machine-readable error codes and human-readable error messages in response body when an error is encountered. 126 | 127 | #### Example 128 | 129 | ##### Validation Error 130 | 131 | ```js 132 | { 133 | "statusCode": 400, 134 | "error": "Bad Request", 135 | "message": "Invalid query parameters", 136 | "data": { 137 | "code": 10003, 138 | "validation": { 139 | "details":[ 140 | { 141 | "message": "\"name\" is required", 142 | "path": "name", 143 | "type": "any.required", 144 | "context": { 145 | "key": "name" 146 | } 147 | },{ 148 | "message": "\"email\" must be a valid email", 149 | "path": "email", 150 | "type": "string.email", 151 | "context": { 152 | "value": "foo", 153 | "key": "email" 154 | } 155 | } 156 | ], 157 | "source": "query", 158 | "keys": [ "name","email" ] 159 | } 160 | } 161 | } 162 | ``` 163 | 164 | ##### Generic Error 165 | 166 | ```js 167 | { 168 | "statusCode": 403, 169 | "error": "Forbidden", 170 | "message": "Your account is suspended and is not permitted to access this feature", 171 | "data": { 172 | "code": 13003 173 | } 174 | } 175 | ``` 176 | 177 | #### Error Codes Dictionary 178 | 179 | + `10003` - Invalid query parameters 180 | + `10005` - Date is not in ISO 8601 standard 181 | + `10010` - Invalid Content-Type 182 | + `10011` - Invalid User-Agent 183 | + `10012` - Invalid or missing Application-Id 184 | + `11001` - Invalid or expired token 185 | + `11003` - Bad authentication data - *Method requires authentication but it was not presented or was wholly invalid.* 186 | + `11005` - Account not allowed to access this endpoint 187 | + `13003` - Your account is suspended and is not permitted to access this feature 188 | 189 | ## Versioning 190 | 191 | This API uses `Api-Version` header to identify requested version. Every **minor** version SHOULD be backward compatible. However, **major** versions MAY introduce *breaking changes*. 192 | 193 | `Api-Version: 1.0.0` 194 | 195 | This header SHOULD be present in every request. If not, API MUST use the newest available major release. 196 | 197 | If requested version is not available, API SHOULD try to fall back to the next available minor release. 198 | 199 | # Group Authentication 200 | 201 | ## User login [/auth/login] 202 | 203 | Access tokens are required to access nearly all endpoints of this API. 204 | 205 | ### Retrieve a token [POST] 206 | 207 | Allows to retrieve a valid JSON Web Token for username and password. 208 | 209 | **Endpoint information** 210 | 211 | | | | 212 | |-------------------------|-----| 213 | | Requires authentication | No | 214 | | Has restricted scope | No | 215 | 216 | + Request (application/json) 217 | + Attributes 218 | + login: `john.doe@mail.com` (string, required) - User email address 219 | + password: `QXR0mi38a2` (string, required) - User password 220 | 221 | + Response 200 (application/json) 222 | + Attributes 223 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....` (string) - JSON Web Token. 224 | 225 | ## Refresh a token [POST /auth/refresh-token] 226 | 227 | Allows to retrieve a new, valid JSON Web Token based on a valid JSON Web Token. 228 | 229 | Expired tokens MUST NOT be refreshed. 230 | 231 | **Endpoint information** 232 | 233 | | | | 234 | |-------------------------|-----| 235 | | Requires authentication | Yes | 236 | | Has restricted scope | No | 237 | 238 | + Request 239 | + Headers 240 | 241 | Authorization: JWT 242 | 243 | + Response 200 (application/json) 244 | + Attributes 245 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....` (string) - New JWT 246 | 247 | ## User registration [/auth/register] 248 | 249 | ### Register a new user [POST] 250 | 251 | Creates a new user account. 252 | 253 | + Provided email address MUST be unique. 254 | + Passwords MUST have at least six characters. 255 | + Passwords MUST NOT contain the user name or parts of the user’s full name, such as his first name. 256 | + Passwords MUST use at least three of the four available character types: lowercase letters, uppercase letters, numbers, and symbols. 257 | 258 | After successful registration a confirmation email MUST be sent to provided address. 259 | 260 | **Endpoint information** 261 | 262 | | | | 263 | |-------------------------|-----| 264 | | Requires authentication | No | 265 | 266 | **Error codes** 267 | 268 | | | | | 269 | |-------|---------| ------------------------------------------- | 270 | | `400` | `4001` | Password doesn't match password guidelines | 271 | | `400` | `3001` | User already exists | 272 | 273 | + Request (application/json) 274 | + Attributes 275 | + email: `john.doe@mail.com` (string, required) - E-mail address. 276 | + password: `QXR0mi38a2` (string, required) - User password. 277 | 278 | + Response 201 279 | 280 | # Group User 281 | 282 | ## Current user profile [/users/me] 283 | 284 | Current user MUST be identifed by JWT provided in request header. 285 | 286 | ### Retrieve profile of the current user [GET] 287 | 288 | Retrieves the profile of the current user. 289 | 290 | **Endpoint information** 291 | 292 | | | | 293 | |-------------------------|-----| 294 | | Requires authentication | Yes | 295 | | Has restricted scope | No | 296 | 297 | + Request 298 | + Headers 299 | 300 | Authorization: JWT 301 | 302 | + Response 200 (application/json) 303 | + Attributes (User) 304 | 305 | ### Partialy update a profile of the current user [PATCH] 306 | 307 | Updates a profile of the current user setting the values of the parameters passed. Any parameters not provided will be left unchanged. 308 | 309 | **Endpoint information** 310 | 311 | | | | 312 | |-------------------------|-----| 313 | | Requires authentication | Yes | 314 | | Has restricted scope | No | 315 | 316 | + Request (application/json) 317 | + Headers 318 | 319 | Authorization: JWT 320 | + Attributes 321 | + name: `Ben` (string) - First name of the user. 322 | 323 | + Response 200 (application/json) 324 | + Attributes (User) 325 | 326 | ## User password [/users/me/password] 327 | 328 | ### Change a password of the current user [PUT] 329 | 330 | Changes user password. 331 | 332 | After password is changed all access tokens issued for this user prior to password change must be invalidated. 333 | 334 | **Endpoint information** 335 | 336 | | | | 337 | |-------------------------|-----| 338 | | Requires authentication | Yes | 339 | | Has restricted scope | No | 340 | 341 | **Error codes** 342 | 343 | | | | | 344 | |-------|---------| ----------------------------------------- | 345 | | `400` | `4001` | Password doesn't match password guidelines | 346 | 347 | + Request (application/json) 348 | + Headers 349 | 350 | Authorization: JWT 351 | + Attributes 352 | + old_password: `secret` (string, required) 353 | + new_password: `$3C6e7` (string, required) 354 | 355 | + Response 200 356 | + Attributes 357 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...` (string) - New JSON Web Token. 358 | 359 | ## User avatar [/users/me/avatar] 360 | 361 | ### Set user avatar [POST] 362 | 363 | Sets user avatar. 364 | 365 | Either upload the raw binary ( **media** parameter) of the file, or its base64-encoded contents ( **media_data** parameter). Use raw binary when possible, because base64 encoding results in larger file sizes. 366 | 367 | **Endpoint information** 368 | 369 | | | | 370 | |-------------------------|-----| 371 | | Requires authentication | Yes | 372 | | Has restricted scope | No | 373 | 374 | **Error codes** 375 | 376 | | | | | 377 | |-------|---------| --------------------- | 378 | | `400` | `2001` | File is too large | 379 | | `400` | `2002` | Unsupported file type | 380 | 381 | + Request (multipart/form-data) 382 | + Headers 383 | 384 | Authorization: JWT 385 | 386 | + Response 200 387 | + Attributes 388 | + avatar: `https://...` (string) - Public download URL 389 | 390 | ### Delete user avatar [DELETE] 391 | 392 | Restores user avatar to the default one. 393 | 394 | **Endpoint information** 395 | 396 | | | | 397 | |-------------------------|-----| 398 | | Requires authentication | Yes | 399 | | Has restricted scope | No | 400 | 401 | + Request 402 | + Headers 403 | 404 | Authorization: JWT 405 | 406 | + Response 204 407 | 408 | ## Users [/users] 409 | 410 | ### List all users [GET] 411 | 412 | Returns a list of users. The users are returned in sorter order, with the most recently created user accounts appearing first. 413 | 414 | | | | 415 | |-------------------------|-----| 416 | | Requires authentication | Yes | 417 | | Has restricted scope | No | 418 | 419 | + Request 420 | + Headers 421 | 422 | Authorization: JWT 423 | 424 | + Response 200 (application/json) 425 | + Attributes (array[User]) 426 | 427 | ## User [/users/{id}] 428 | 429 | + Parameters 430 | + id: `a6vhAo3FG7t1` (string) - id of the user. 431 | 432 | ### Retrieve a user [GET] 433 | 434 | Retrieves the details of an existing user. 435 | 436 | | | | 437 | |-------------------------|-----| 438 | | Requires authentication | Yes | 439 | | Has restricted scope | No | 440 | 441 | **Error codes** 442 | 443 | | | | | 444 | |-------|---------| ----------------------------- | 445 | | `400` | `1000` | Referenced resource not found | 446 | 447 | + Request 448 | + Headers 449 | 450 | Authorization: JWT 451 | 452 | + Response 200 (application/json) 453 | + Attributes (User) 454 | 455 | + Response 404 456 | 457 | # Data Structures 458 | 459 | ## Resource (object) 460 | + id: `a6vhAo3FG7t1` (string, fixed) - Short non-sequential url-friendly unique id. 461 | + createdAt: `2016-07-01T15:11:09.553Z` (string) - ISO Date/time string. When this resource was created. 462 | + updatedAt: `2016-07-01T15:11:09.553Z` (string) - ISO Date/time string. When this resource was last updated. 463 | 464 | ## User (Resource) 465 | + email: `john.doe@mail.com` (string, required) - Login. Unique email address of the user. 466 | + facebookId: `888047057953991` (number, optional, nullable) - Facebook ID of the user if user account is linked to the Facebook account. 467 | + name: `John` (string, optional, nullable) - First name of the user. 468 | + surname: `Doe` (string, optional, nullable) - Last name of the user. 469 | + avatar (string, optional, nullable) - URL to user avatar. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "bootstrap-sass": "^3.4.1", 14 | "cross-env": "^5.2.0", 15 | "css-element-queries": "^1.1.1", 16 | "highlight.js": "^10.4.1", 17 | "jquery": "^3.5.0", 18 | "laravel-mix": "^4.0.14", 19 | "resolve-url-loader": "2.3.1", 20 | "sass": "^1.26.3", 21 | "sass-loader": "7.*", 22 | "sticky-sidebar": "^3.3.1", 23 | "vue-template-compiler": "^2.6.7" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /public/css/blueprintdocs.css: -------------------------------------------------------------------------------- 1 | .hljs-comment,.hljs-quote{color:#8e908c}.hljs-attr,.hljs-attribute,.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#c82829}.hljs-built_in,.hljs-builtin-name,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#f5871f}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#718c00}.hljs-section,.hljs-title{color:#4271ae}.hljs-keyword,.hljs-selector-tag{color:#8959a8}.hljs{display:block;overflow-x:auto;background:transparent;color:#4d4d4c;padding:.5em}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}pre{border:1px solid #e6e6e6}.sidebar{display:none;float:left;margin:0 15px;will-change:min-height}@media (min-width:992px){.sidebar{display:block;width:293px}}@media (min-width:1200px){.sidebar{display:block;width:360px}}.sidebar__inner{position:relative;-webkit-transform:translate(0);transform:translate(0);-webkit-transform:translateZ(0);transform:translateZ(0);will-change:position,transform;overflow:auto}.host{margin-bottom:22px;text-align:center;word-wrap:break-word}.main{margin-left:0;padding:0 15px}@media (min-width:992px){.main{margin-left:323px}}@media (min-width:1200px){.main{margin-left:390px}}.panel{border:1px solid #d3e0e9;overflow:hidden}.panel-group .panel{margin-bottom:22px;border-radius:4px}.panel-collapsable .panel-heading{padding:10px 15px;color:#333;border-color:#d3e0e9;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px;background-color:#f5f5f5}.panel-collapsable .panel-title{font-size:15px;line-height:1.6}.panel-collapsable .panel-title a{position:relative}.panel-collapsable .panel-title a:active,.panel-collapsable .panel-title a:hover,.panel-collapsable .panel-title a:link,.panel-collapsable .panel-title a:visited{text-decoration:none}.panel-collapsable .panel-title a.collapsed:after{content:"+";line-height:1}.panel-collapsable .panel-body{padding:0}.panel-body,.stacked-tabs{font-weight:300}.stacked-tabs{border-radius:4px}.stacked-tabs a{border-top:1px solid #e4ecf2;border-left:2px solid transparent;color:#636b6f}.stacked-tabs li a:active,.stacked-tabs li a:hover,.stacked-tabs li a:link,.stacked-tabs li a:visited{background-color:#fff}.stacked-tabs li a:hover{border-left-color:#e4ecf2}.stacked-tabs ul li a{padding-left:30px}.stacked-tabs li .method{display:inline-block;float:right}.panel-description .panel-title{font-size:36px;line-height:1.8;color:#636b6f}.panel-resource-group>.panel-heading .panel-title{font-size:18px;line-height:1.6}.panel-description h2{margin-top:30px;font-size:23px}.panel-description h3{margin-top:30px;font-size:20px}.panel-description h4{font-size:16px;font-weight:700;margin-top:30px}.panel-default>.panel-heading{color:inherit}.panel-action .panel-title{font-size:15px;line-height:1.6}.panel-action .panel-title .name{float:right;padding:3px 0}.panel-action .panel-title .method{display:inline-block;margin-right:12px;padding:3px 12px;font-size:14px;font-weight:600;border-radius:3px}.panel-action .panel-title .method.get,.panel-action .panel-title .method.head,.panel-action .panel-title .method.options{color:#fff;background-color:#337ab7}.panel-action .panel-title .method.patch,.panel-action .panel-title .method.put{color:#fff;background-color:#ed9c28}.panel-action .panel-title .method.post{color:#fff;background-color:#5cb85c}.panel-action .panel-title .method.delete{color:#fff;background-color:#d9534f}.panel-action .definition{margin:0 0 11px}.panel-action .definition .uri{color:#000}.panel-action .definition .uri .hostname{color:#636b6f}.panel-action .panel-footer{background-color:#fff}.panel-action .panel-footer .nav-pills>li.active>a,.panel-action .panel-footer .nav-pills>li.active>a:hover{color:#216a94;background-color:#f5f5f5}.panel-action .panel-footer .tab-pane.active>:first-child{margin-top:15px}.main ul li p{margin:0}.main table{margin:0 0 11px}.main table td,.main table th{padding:6px 12px;vertical-align:top;border:1px solid #e6e6e6}.panel-collapsable .panel-title a:after{content:"-";position:absolute;top:0;right:0;font-size:25px;font-weight:200;line-height:.8}.main .parameters table{width:100%;margin-top:10px}.main .parameters table p{margin:0}.method{margin-right:5px;font-weight:600}.method.get,.method.head,.method.options{color:#337ab7}.method.patch,.method.put{color:#ed9c28}.method.post{color:#5cb85c}.method.delete{color:#d9534f} -------------------------------------------------------------------------------- /public/css/vendor.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#fdf6e3;color:#657b83}.hljs-comment,.hljs-quote{color:#93a1a1}.hljs-addition,.hljs-keyword,.hljs-selector-tag{color:#859900}.hljs-doctag,.hljs-literal,.hljs-meta .hljs-meta-string,.hljs-number,.hljs-regexp,.hljs-string{color:#2aa198}.hljs-name,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-title{color:#268bd2}.hljs-attr,.hljs-attribute,.hljs-class .hljs-title,.hljs-template-variable,.hljs-type,.hljs-variable{color:#b58900}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-subst,.hljs-symbol{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#eee8d5}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M165437/laravel-blueprint-docs/cea446aa6a78ce6a6b0f086fb0cc65c4bd9264ff/public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M165437/laravel-blueprint-docs/cea446aa6a78ce6a6b0f086fb0cc65c4bd9264ff/public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M165437/laravel-blueprint-docs/cea446aa6a78ce6a6b0f086fb0cc65c4bd9264ff/public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M165437/laravel-blueprint-docs/cea446aa6a78ce6a6b0f086fb0cc65c4bd9264ff/public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /public/js/blueprintdocs.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=4)}([,,,,function(e,t,n){e.exports=n(5)},function(e,t,n){window.ResizeSensor=n(6),n(7);var i=n(8);i.registerLanguage("http",n(9)),i.registerLanguage("json",n(10)),$(document).ready(function(){var e=new StickySidebar(".sidebar",{topSpacing:22,bottomSpacing:0,containerSelector:".content-wrapper",innerWrapperSelector:".sidebar__inner",resizeSensor:!0});$(window).resize(function(){e.updateSticky()}),$("[id^=collapse-]").on("show.bs.collapse",function(){var e=$(this).prev().find("a").data("group-id"),t=$(document.getElementById(e)).offset().top-33;$("html, body").animate({scrollTop:t})}),$("[id^=collapse-]").on("hide.bs.collapse",function(e){var t=$(this).prev().find("a").data("group-id"),n=$(document.getElementById(t)).offset().top-33;$("html, body").animate({scrollTop:n}),($(window).scrollTop()n+1)&&e.preventDefault()}),$(".tabs").on("click","a",function(e){var t=$(this).attr("href").substring(1),n=$(document.getElementById(t)).offset().top-33;$("html, body").animate({scrollTop:n}),e.preventDefault()}),$(".nav-pills").on("click",".active a",function(e){var t=$(this);e.preventDefault(),window.setTimeout(function(){t.closest(".nav-pills").next(".tab-content").find(".tab-pane").removeClass("active"),t.parent("li").removeClass("active")},0)}),$("pre code").each(function(e,t){i.highlightBlock(t)})})},function(e,t,n){"use strict";var i,r;"undefined"!=typeof window&&window,void 0===(r="function"==typeof(i=function(){if("undefined"==typeof window)return null;var e=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(e){return window.setTimeout(e,20)};function t(e,t){var n=Object.prototype.toString.call(e),i="[object Array]"===n||"[object NodeList]"===n||"[object HTMLCollection]"===n||"[object Object]"===n||"undefined"!=typeof jQuery&&e instanceof jQuery||"undefined"!=typeof Elements&&e instanceof Elements,r=0,s=e.length;if(i)for(;r
',t.appendChild(t.resizeSensor);var a=window.getComputedStyle(t),c=a?a.getPropertyValue("position"):null;"absolute"!==c&&"relative"!==c&&"fixed"!==c&&(t.style.position="relative");var l,d,u=t.resizeSensor.childNodes[0],p=u.childNodes[0],f=t.resizeSensor.childNodes[1],h=n(t),g=h.width,v=h.height,b=!0,m=0,w=function(){if(b){var n=0===t.offsetWidth&&0===t.offsetHeight;if(n)return void(m||(m=e(function(){m=0,w()})));b=!1}var i,r;i=t.offsetWidth,r=t.offsetHeight,p.style.width=i+10+"px",p.style.height=r+10+"px",u.scrollLeft=i+10,u.scrollTop=r+10,f.scrollLeft=i+10,f.scrollTop=r+10};t.resizeSensor.resetSensor=w;var E=function(){d=0,l&&(g=h.width,v=h.height,t.resizedAttached&&t.resizedAttached.call(h))},y=function(){h=n(t),(l=h.width!==g||h.height!==v)&&!d&&(d=e(E)),w()},S=function(e,t,n){e.attachEvent?e.attachEvent("on"+t,n):e.addEventListener(t,n)};S(u,"scroll",y),S(f,"scroll",y),e(w)}}t(r,function(e){a(e,s)}),this.detach=function(e){i.detach(r,e)},this.reset=function(){r.resizeSensor.resetSensor()}};if(i.reset=function(e){t(e,function(e){e.resizeSensor.resetSensor()})},i.detach=function(e,n){t(e,function(e){e&&(e.resizedAttached&&"function"==typeof n&&(e.resizedAttached.remove(n),e.resizedAttached.length())||e.resizeSensor&&(e.contains(e.resizeSensor)&&e.removeChild(e.resizeSensor),delete e.resizeSensor,delete e.resizedAttached))})},"undefined"!=typeof MutationObserver){var r=new MutationObserver(function(e){for(var t in e)if(e.hasOwnProperty(t))for(var n=e[t].addedNodes,r=0;r1&&void 0!==arguments[1]?arguments[1]:{};if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,i),this.options=i.extend(n,r),this.sidebar="string"==typeof e?document.querySelector(e):e,void 0===this.sidebar)throw new Error("There is no specific sidebar element.");this.sidebarInner=!1,this.container=this.sidebar.parentElement,this.affixedType="STATIC",this.direction="down",this.support={transform:!1,transform3d:!1},this._initialized=!1,this._reStyle=!1,this._breakpoint=!1,this._resizeListeners=[],this.dimensions={translateY:0,topSpacing:0,lastTopSpacing:0,bottomSpacing:0,lastBottomSpacing:0,sidebarHeight:0,sidebarWidth:0,containerTop:0,containerHeight:0,viewportHeight:0,viewportTop:0,lastViewportTop:0},["handleEvent"].forEach(function(e){t[e]=t[e].bind(t)}),this.initialize()}return e(i,[{key:"initialize",value:function(){var e=this;if(this._setSupportFeatures(),this.options.innerWrapperSelector&&(this.sidebarInner=this.sidebar.querySelector(this.options.innerWrapperSelector),null===this.sidebarInner&&(this.sidebarInner=!1)),!this.sidebarInner){var t=document.createElement("div");for(t.setAttribute("class","inner-wrapper-sticky"),this.sidebar.appendChild(t);this.sidebar.firstChild!=t;)t.appendChild(this.sidebar.firstChild);this.sidebarInner=this.sidebar.querySelector(".inner-wrapper-sticky")}if(this.options.containerSelector){var n=document.querySelectorAll(this.options.containerSelector);if((n=Array.prototype.slice.call(n)).forEach(function(t,n){t.contains(e.sidebar)&&(e.container=t)}),!n.length)throw new Error("The container does not contains on the sidebar.")}"function"!=typeof this.options.topSpacing&&(this.options.topSpacing=parseInt(this.options.topSpacing)||0),"function"!=typeof this.options.bottomSpacing&&(this.options.bottomSpacing=parseInt(this.options.bottomSpacing)||0),this._widthBreakpoint(),this.calcDimensions(),this.stickyPosition(),this.bindEvents(),this._initialized=!0}},{key:"bindEvents",value:function(){window.addEventListener("resize",this,{passive:!0,capture:!1}),window.addEventListener("scroll",this,{passive:!0,capture:!1}),this.sidebar.addEventListener("update"+t,this),this.options.resizeSensor&&"undefined"!=typeof ResizeSensor&&(new ResizeSensor(this.sidebarInner,this.handleEvent),new ResizeSensor(this.container,this.handleEvent))}},{key:"handleEvent",value:function(e){this.updateSticky(e)}},{key:"calcDimensions",value:function(){if(!this._breakpoint){var e=this.dimensions;e.containerTop=i.offsetRelative(this.container).top,e.containerHeight=this.container.clientHeight,e.containerBottom=e.containerTop+e.containerHeight,e.sidebarHeight=this.sidebarInner.offsetHeight,e.sidebarWidth=this.sidebar.offsetWidth,e.viewportHeight=window.innerHeight,this._calcDimensionsWithScroll()}}},{key:"_calcDimensionsWithScroll",value:function(){var e=this.dimensions;e.sidebarLeft=i.offsetRelative(this.sidebar).left,e.viewportTop=document.documentElement.scrollTop||document.body.scrollTop,e.viewportBottom=e.viewportTop+e.viewportHeight,e.viewportLeft=document.documentElement.scrollLeft||document.body.scrollLeft,e.topSpacing=this.options.topSpacing,e.bottomSpacing=this.options.bottomSpacing,"function"==typeof e.topSpacing&&(e.topSpacing=parseInt(e.topSpacing(this.sidebar))||0),"function"==typeof e.bottomSpacing&&(e.bottomSpacing=parseInt(e.bottomSpacing(this.sidebar))||0),"VIEWPORT-TOP"===this.affixedType?e.topSpacing=e.containerBottom?(e.translateY=e.containerBottom-n,t="CONTAINER-BOTTOM"):i>=e.containerTop&&(e.translateY=i-e.containerTop,t="VIEWPORT-TOP"):e.containerBottom<=r?(e.translateY=e.containerBottom-n,t="CONTAINER-BOTTOM"):n+e.translateY<=r?(e.translateY=r-n,t="VIEWPORT-BOTTOM"):e.containerTop+e.translateY<=i&&(t="VIEWPORT-UNBOTTOM"),e.translateY=Math.max(0,e.translateY),e.translateY=Math.min(e.containerHeight,e.translateY),e.lastViewportTop=e.viewportTop,t}},{key:"_getStyle",value:function(e){if(void 0!==e){var t={inner:{},outer:{}},n=this.dimensions;switch(e){case"VIEWPORT-TOP":t.inner={position:"fixed",top:n.topSpacing,left:n.sidebarLeft-n.viewportLeft,width:n.sidebarWidth};break;case"VIEWPORT-BOTTOM":t.inner={position:"fixed",top:"auto",left:n.sidebarLeft,bottom:n.bottomSpacing,width:n.sidebarWidth};break;case"CONTAINER-BOTTOM":case"VIEWPORT-UNBOTTOM":var r=this._getTranslate(0,n.translateY+"px");t.inner=r?{transform:r}:{position:"absolute",top:n.translateY,width:n.sidebarWidth}}switch(e){case"VIEWPORT-TOP":case"VIEWPORT-BOTTOM":case"VIEWPORT-UNBOTTOM":case"CONTAINER-BOTTOM":t.outer={height:n.sidebarHeight,position:"relative"}}return t.outer=i.extend({height:"",position:""},t.outer),t.inner=i.extend({position:"relative",top:"",left:"",bottom:"",width:"",transform:this._getTranslate()},t.inner),t}}},{key:"stickyPosition",value:function(e){if(!this._breakpoint){e=this._reStyle||e||!1;var n=this.getAffixType(),r=this._getStyle(n);if((this.affixedType!=n||e)&&n){var s="affix."+n.toLowerCase().replace("viewport-","")+t;for(var o in i.eventTrigger(this.sidebar,s),"STATIC"===n?i.removeClass(this.sidebar,this.options.stickyClass):i.addClass(this.sidebar,this.options.stickyClass),r.outer)this.sidebar.style[o]=r.outer[o];for(var a in r.inner){var c="number"==typeof r.inner[a]?"px":"";this.sidebarInner.style[a]=r.inner[a]+c}var l="affixed."+n.toLowerCase().replace("viewport-","")+t;i.eventTrigger(this.sidebar,l)}else this._initialized&&(this.sidebarInner.style.left=r.inner.left);this.affixedType=n}}},{key:"_widthBreakpoint",value:function(){window.innerWidth<=this.options.minWidth?(this._breakpoint=!0,this.affixedType="STATIC",this.sidebar.removeAttribute("style"),i.removeClass(this.sidebar,this.options.stickyClass),this.sidebarInner.removeAttribute("style")):this._breakpoint=!1}},{key:"updateSticky",value:function(){var e,t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this._running||(this._running=!0,e=n.type,requestAnimationFrame(function(){switch(e){case"scroll":t._calcDimensionsWithScroll(),t.observeScrollDir(),t.stickyPosition();break;case"resize":default:t._widthBreakpoint(),t.calcDimensions(),t.stickyPosition(!0)}t._running=!1}))}},{key:"_setSupportFeatures",value:function(){var e=this.support;e.transform=i.supportTransform(),e.transform3d=i.supportTransform(!0)}},{key:"_getTranslate",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return this.support.transform3d?"translate3d("+e+", "+t+", "+n+")":!!this.support.translate&&"translate("+e+", "+t+")"}},{key:"destroy",value:function(){window.removeEventListener("resize",this,{caption:!1}),window.removeEventListener("scroll",this,{caption:!1}),this.sidebar.classList.remove(this.options.stickyClass),this.sidebar.style.minHeight="",this.sidebar.removeEventListener("update"+t,this);var e={inner:{},outer:{}};for(var n in e.inner={position:"",top:"",left:"",bottom:"",width:"",transform:""},e.outer={height:"",position:""},e.outer)this.sidebar.style[n]=e.outer[n];for(var i in e.inner)this.sidebarInner.style[i]=e.inner[i];this.options.resizeSensor&&"undefined"!=typeof ResizeSensor&&(ResizeSensor.detach(this.sidebarInner,this.handleEvent),ResizeSensor.detach(this.container,this.handleEvent))}}],[{key:"supportTransform",value:function(e){var t=!1,n=e?"perspective":"transform",i=n.charAt(0).toUpperCase()+n.slice(1),r=document.createElement("support"),s=r.style;return(n+" "+["Webkit","Moz","O","ms"].join(i+" ")+i).split(" ").forEach(function(e,n){if(void 0!==s[e])return t=e,!1}),t}},{key:"eventTrigger",value:function(e,t,n){try{var i=new CustomEvent(t,{detail:n})}catch(e){var i=document.createEvent("CustomEvent");i.initCustomEvent(t,!0,!0,n)}e.dispatchEvent(i)}},{key:"extend",value:function(e,t){var n={};for(var i in e)void 0!==t[i]?n[i]=t[i]:n[i]=e[i];return n}},{key:"offsetRelative",value:function(e){var t={left:0,top:0};do{var n=e.offsetTop,i=e.offsetLeft;isNaN(n)||(t.top+=n),isNaN(i)||(t.left+=i),e="BODY"===e.tagName?e.parentElement:e.offsetParent}while(e);return t}},{key:"addClass",value:function(e,t){i.hasClass(e,t)||(e.classList?e.classList.add(t):e.className+=" "+t)}},{key:"removeClass",value:function(e,t){i.hasClass(e,t)&&(e.classList?e.classList.remove(t):e.className=e.className.replace(new RegExp("(^|\\b)"+t.split(" ").join("|")+"(\\b|$)","gi")," "))}},{key:"hasClass",value:function(e,t){return e.classList?e.classList.contains(t):new RegExp("(^| )"+t+"( |$)","gi").test(e.className)}}]),i}();return i}();return window.StickySidebar=t,t}()},function(e,t,n){!function(e){"object"==typeof window&&window||"object"==typeof self&&self;(function(e){var t,n=[],i=Object.keys,r={},s={},o=/^(no-?highlight|plain|text)$/i,a=/\blang(?:uage)?-([\w-]+)\b/i,c=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,l="",d={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function u(e){return e.replace(/&/g,"&").replace(//g,">")}function p(e){return e.nodeName.toLowerCase()}function f(e,t){var n=e&&e.exec(t);return n&&0===n.index}function h(e){return o.test(e)}function g(e){var t,n={},i=Array.prototype.slice.call(arguments,1);for(t in e)n[t]=e[t];return i.forEach(function(e){for(t in e)n[t]=e[t]}),n}function v(e){var t=[];return function e(n,i){for(var r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?i+=r.nodeValue.length:1===r.nodeType&&(t.push({event:"start",offset:i,node:r}),i=e(r,i),p(r).match(/br|hr|img|input/)||t.push({event:"stop",offset:i,node:r}));return i}(e,0),t}function b(e){if(t&&!e.langApiRestored){for(var n in e.langApiRestored=!0,t)e[n]&&(e[t[n]]=e[n]);(e.contains||[]).concat(e.variants||[]).forEach(b)}}function m(e){function t(e){return e&&e.source||e}function n(n,i){return new RegExp(t(n),"m"+(e.case_insensitive?"i":"")+(i?"g":""))}!function r(s,o){if(s.compiled)return;s.compiled=!0;s.keywords=s.keywords||s.beginKeywords;if(s.keywords){var a={},c=function(t,n){e.case_insensitive&&(n=n.toLowerCase()),n.split(" ").forEach(function(e){var n=e.split("|");a[n[0]]=[t,n[1]?Number(n[1]):1]})};"string"==typeof s.keywords?c("keyword",s.keywords):i(s.keywords).forEach(function(e){c(e,s.keywords[e])}),s.keywords=a}s.lexemesRe=n(s.lexemes||/\w+/,!0);o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")\\b"),s.begin||(s.begin=/\B|\b/),s.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(s.endRe=n(s.end)),s.terminator_end=t(s.end)||"",s.endsWithParent&&o.terminator_end&&(s.terminator_end+=(s.end?"|":"")+o.terminator_end));s.illegal&&(s.illegalRe=n(s.illegal));null==s.relevance&&(s.relevance=1);s.contains||(s.contains=[]);s.contains=Array.prototype.concat.apply([],s.contains.map(function(e){return function(e){e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map(function(t){return g(e,{variants:null},t)}));return e.cached_variants||e.endsWithParent&&[g(e)]||[e]}("self"===e?s:e)}));s.contains.forEach(function(e){r(e,s)});s.starts&&r(s.starts,o);var l=s.contains.map(function(e){return e.beginKeywords?"\\.?(?:"+e.begin+")\\.?":e.begin}).concat([s.terminator_end,s.illegal]).map(t).filter(Boolean);s.terminators=l.length?n(function(e,n){for(var i=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,s="",o=0;o0&&(s+=n);c.length>0;){var l=i.exec(c);if(null==l){s+=c;break}s+=c.substring(0,l.index),c=c.substring(l.index+l[0].length),"\\"==l[0][0]&&l[1]?s+="\\"+String(Number(l[1])+a):(s+=l[0],"("==l[0]&&r++)}}return s}(l,"|"),!0):{exec:function(){return null}}}(e)}function w(e,t,n,i){function s(e,t){var n=h.case_insensitive?t[0].toLowerCase():t[0];return e.keywords.hasOwnProperty(n)&&e.keywords[n]}function o(e,t,n,i){var r=i?"":d.classPrefix,s='')+t+o}function a(){y+=null!=v.subLanguage?function(){var e="string"==typeof v.subLanguage;if(e&&!r[v.subLanguage])return u(S);var t=e?w(v.subLanguage,S,!0,b[v.subLanguage]):E(S,v.subLanguage.length?v.subLanguage:void 0);v.relevance>0&&(T+=t.relevance);e&&(b[v.subLanguage]=t.top);return o(t.language,t.value,!1,!0)}():function(){var e,t,n,i;if(!v.keywords)return u(S);i="",t=0,v.lexemesRe.lastIndex=0,n=v.lexemesRe.exec(S);for(;n;)i+=u(S.substring(t,n.index)),(e=s(v,n))?(T+=e[1],i+=o(e[0],u(n[0]))):i+=u(n[0]),t=v.lexemesRe.lastIndex,n=v.lexemesRe.exec(S);return i+u(S.substr(t))}(),S=""}function c(e){y+=e.className?o(e.className,"",!0):"",v=Object.create(e,{parent:{value:v}})}function p(e,t){if(S+=e,null==t)return a(),0;var i=function(e,t){var n,i;for(n=0,i=t.contains.length;n")+'"');return S+=t,t.length||1}var h=_(e);if(!h)throw new Error('Unknown language: "'+e+'"');m(h);var g,v=i||h,b={},y="";for(g=v;g!==h;g=g.parent)g.className&&(y=o(g.className,"",!0)+y);var S="",T=0;try{for(var R,O,N=0;v.terminators.lastIndex=N,R=v.terminators.exec(t);)O=p(t.substring(N,R.index),R[0]),N=R.index+O;for(p(t.substr(N)),g=v;g.parent;g=g.parent)g.className&&(y+=l);return{relevance:T,value:y,language:e,top:v}}catch(e){if(e.message&&-1!==e.message.indexOf("Illegal"))return{relevance:0,value:u(t)};throw e}}function E(e,t){t=t||d.languages||i(r);var n={relevance:0,value:u(e)},s=n;return t.filter(_).filter(R).forEach(function(t){var i=w(t,e,!1);i.language=t,i.relevance>s.relevance&&(s=i),i.relevance>n.relevance&&(s=n,n=i)}),s.language&&(n.second_best=s),n}function y(e){return d.tabReplace||d.useBR?e.replace(c,function(e,t){return d.useBR&&"\n"===e?"
":d.tabReplace?t.replace(/\t/g,d.tabReplace):""}):e}function S(e){var t,i,r,o,c,l=function(e){var t,n,i,r,s=e.className+" ";if(s+=e.parentNode?e.parentNode.className:"",n=a.exec(s))return _(n[1])?n[1]:"no-highlight";for(s=s.split(/\s+/),t=0,i=s.length;t/g,"\n"):t=e,c=t.textContent,r=l?w(l,c,!0):E(c),(i=v(t)).length&&((o=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=function(e,t,i){var r=0,s="",o=[];function a(){return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset"}function l(e){s+=""}function d(e){("start"===e.event?c:l)(e.node)}for(;e.length||t.length;){var f=a();if(s+=u(i.substring(r,f[0].offset)),r=f[0].offset,f===e){o.reverse().forEach(l);do{d(f.splice(0,1)[0]),f=a()}while(f===e&&f.length&&f[0].offset===r);o.reverse().forEach(c)}else"start"===f[0].event?o.push(f[0].node):o.pop(),d(f.splice(0,1)[0])}return s+u(i.substr(r))}(i,v(o),c)),r.value=y(r.value),e.innerHTML=r.value,e.className=function(e,t,n){var i=t?s[t]:n,r=[e.trim()];e.match(/\bhljs\b/)||r.push("hljs");-1===e.indexOf(i)&&r.push(i);return r.join(" ").trim()}(e.className,l,r.language),e.result={language:r.language,re:r.relevance},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.relevance}))}function T(){if(!T.called){T.called=!0;var e=document.querySelectorAll("pre code");n.forEach.call(e,S)}}function _(e){return e=(e||"").toLowerCase(),r[e]||r[s[e]]}function R(e){var t=_(e);return t&&!t.disableAutodetect}e.highlight=w,e.highlightAuto=E,e.fixMarkup=y,e.highlightBlock=S,e.configure=function(e){d=g(d,e)},e.initHighlighting=T,e.initHighlightingOnLoad=function(){addEventListener("DOMContentLoaded",T,!1),addEventListener("load",T,!1)},e.registerLanguage=function(t,n){var i=r[t]=n(e);b(i),i.aliases&&i.aliases.forEach(function(e){s[e]=t})},e.listLanguages=function(){return i(r)},e.getLanguage=_,e.autoDetection=R,e.inherit=g,e.IDENT_RE="[a-zA-Z]\\w*",e.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",e.NUMBER_RE="\\b\\d+(\\.\\d+)?",e.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BINARY_NUMBER_RE="\\b(0b[01]+)",e.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BACKSLASH_ESCAPE={begin:"\\\\[\\s\\S]",relevance:0},e.APOS_STRING_MODE={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.QUOTE_STRING_MODE={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.PHRASAL_WORDS_MODE={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.COMMENT=function(t,n,i){var r=e.inherit({className:"comment",begin:t,end:n,contains:[]},i||{});return r.contains.push(e.PHRASAL_WORDS_MODE),r.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|XXX):",relevance:0}),r},e.C_LINE_COMMENT_MODE=e.COMMENT("//","$"),e.C_BLOCK_COMMENT_MODE=e.COMMENT("/\\*","\\*/"),e.HASH_COMMENT_MODE=e.COMMENT("#","$"),e.NUMBER_MODE={className:"number",begin:e.NUMBER_RE,relevance:0},e.C_NUMBER_MODE={className:"number",begin:e.C_NUMBER_RE,relevance:0},e.BINARY_NUMBER_MODE={className:"number",begin:e.BINARY_NUMBER_RE,relevance:0},e.CSS_NUMBER_MODE={className:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},e.REGEXP_MODE={className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0,contains:[e.BACKSLASH_ESCAPE]}]},e.TITLE_MODE={className:"title",begin:e.IDENT_RE,relevance:0},e.UNDERSCORE_TITLE_MODE={className:"title",begin:e.UNDERSCORE_IDENT_RE,relevance:0},e.METHOD_GUARD={begin:"\\.\\s*"+e.UNDERSCORE_IDENT_RE,relevance:0}})(t)}()},function(e,t){e.exports=function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],illegal:"\\S",contains:[{begin:"^"+t,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+t+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:t},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}},function(e,t){e.exports=function(e){var t={literal:"true false null"},n=[e.QUOTE_STRING_MODE,e.C_NUMBER_MODE],i={end:",",endsWithParent:!0,excludeEnd:!0,contains:n,keywords:t},r={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE],illegal:"\\n"},e.inherit(i,{begin:/:/})],illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[e.inherit(i)],illegal:"\\S"};return n.splice(n.length,0,r,s),{contains:n,keywords:t,illegal:"\\S"}}}]); -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/app.css": "/css/app.css", 4 | "/css/blueprintdocs.css": "/css/blueprintdocs.css", 5 | "/js/blueprintdocs.js": "/js/blueprintdocs.js" 6 | } 7 | -------------------------------------------------------------------------------- /resources/assets/js/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We'll load jQuery and the Bootstrap jQuery plugin which provides support 3 | * for JavaScript based Bootstrap features such as modals and tabs. This 4 | * code may be modified to fit the specific needs of your application. 5 | */ 6 | 7 | try { 8 | window.$ = window.jQuery = require('jquery'); 9 | 10 | require('bootstrap-sass'); 11 | } catch (e) {} -------------------------------------------------------------------------------- /resources/assets/js/blueprintdocs.js: -------------------------------------------------------------------------------- 1 | window.ResizeSensor = require('css-element-queries/src/ResizeSensor'); 2 | require('sticky-sidebar/dist/sticky-sidebar.js'); 3 | 4 | var hljs = require('highlight.js/lib/highlight'); 5 | hljs.registerLanguage('http', require('highlight.js/lib/languages/http')); 6 | hljs.registerLanguage('json', require('highlight.js/lib/languages/json')); 7 | 8 | $(document).ready(function () { 9 | var sidebar = new StickySidebar('.sidebar', { 10 | topSpacing: 22, 11 | bottomSpacing: 0, 12 | containerSelector: '.content-wrapper', 13 | innerWrapperSelector: '.sidebar__inner', 14 | resizeSensor: true 15 | }); 16 | 17 | $(window).resize(function() { 18 | sidebar.updateSticky(); 19 | }); 20 | 21 | $('[id^=collapse-]').on('show.bs.collapse', function () { 22 | var id = $(this).prev().find('a').data('group-id'), 23 | scrollTop = $(document.getElementById(id)).offset().top - 33; 24 | 25 | $('html, body').animate({scrollTop: scrollTop}); 26 | }); 27 | 28 | $('[id^=collapse-]').on('hide.bs.collapse', function (e) { 29 | var id = $(this).prev().find('a').data('group-id'), 30 | scrollTop = $(document.getElementById(id)).offset().top - 33; 31 | 32 | $('html, body').animate({scrollTop: scrollTop}); 33 | 34 | if ($(window).scrollTop() < scrollTop - 1 || $(window).scrollTop() > scrollTop + 1) { 35 | e.preventDefault(); 36 | } 37 | }); 38 | 39 | $('.tabs').on('click', 'a', function (e) { 40 | var id = $(this).attr('href').substring(1), 41 | scrollTop = $(document.getElementById(id)).offset().top - 33; 42 | 43 | $('html, body').animate({scrollTop: scrollTop}); 44 | 45 | e.preventDefault(); 46 | }); 47 | 48 | $('.nav-pills').on('click', '.active a', function (e) { 49 | var that = $(this); 50 | 51 | e.preventDefault(); 52 | 53 | window.setTimeout(function () { 54 | that.closest('.nav-pills').next('.tab-content').find('.tab-pane').removeClass('active'); 55 | that.parent('li').removeClass('active'); 56 | }, 0); 57 | }); 58 | 59 | $('pre code').each(function (i, block) { 60 | hljs.highlightBlock(block); 61 | }); 62 | }); -------------------------------------------------------------------------------- /resources/assets/sass/_highlight.scss: -------------------------------------------------------------------------------- 1 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 2 | 3 | /* Tomorrow Comment */ 4 | .hljs-comment, 5 | .hljs-quote { 6 | color: #8e908c; 7 | } 8 | 9 | /* Tomorrow Red */ 10 | .hljs-variable, 11 | .hljs-attr, 12 | .hljs-attribute, 13 | .hljs-template-variable, 14 | .hljs-tag, 15 | .hljs-name, 16 | .hljs-selector-id, 17 | .hljs-selector-class, 18 | .hljs-regexp, 19 | .hljs-deletion { 20 | color: #c82829; 21 | } 22 | 23 | /* Tomorrow Orange */ 24 | .hljs-number, 25 | .hljs-built_in, 26 | .hljs-builtin-name, 27 | .hljs-literal, 28 | .hljs-type, 29 | .hljs-params, 30 | .hljs-meta, 31 | .hljs-link { 32 | color: #f5871f; 33 | } 34 | 35 | /* Tomorrow Yellow */ 36 | //.hljs-attribute { 37 | // color: #eab700; 38 | //} 39 | 40 | /* Tomorrow Green */ 41 | .hljs-string, 42 | .hljs-symbol, 43 | .hljs-bullet, 44 | .hljs-addition { 45 | color: #718c00; 46 | } 47 | 48 | /* Tomorrow Blue */ 49 | .hljs-title, 50 | .hljs-section { 51 | color: #4271ae; 52 | } 53 | 54 | /* Tomorrow Purple */ 55 | .hljs-keyword, 56 | .hljs-selector-tag { 57 | color: #8959a8; 58 | } 59 | 60 | .hljs { 61 | display: block; 62 | overflow-x: auto; 63 | background: transparent; 64 | color: #4d4d4c; 65 | padding: 0.5em; 66 | } 67 | 68 | .hljs-emphasis { 69 | font-style: italic; 70 | } 71 | 72 | .hljs-strong { 73 | font-weight: bold; 74 | } 75 | -------------------------------------------------------------------------------- /resources/assets/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // Body 3 | $body-bg: #f5f8fa; 4 | 5 | // Borders 6 | $laravel-border-color: darken($body-bg, 10%); 7 | $list-group-border: $laravel-border-color; 8 | $navbar-default-border: $laravel-border-color; 9 | $panel-default-border: $laravel-border-color; 10 | $panel-inner-border: $laravel-border-color; 11 | 12 | // Brands 13 | $brand-primary: #3097D1; 14 | $brand-info: #8eb4cb; 15 | $brand-success: #2ab27b; 16 | $brand-warning: #cbb956; 17 | $brand-danger: #bf5329; 18 | 19 | // Typography 20 | $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; 21 | $font-family-sans-serif: "Raleway", sans-serif; 22 | $font-size-base: 14px; 23 | $line-height-base: 1.6; 24 | $text-color: #636b6f; 25 | 26 | // Navbar 27 | $navbar-default-bg: #fff; 28 | 29 | // Buttons 30 | $btn-default-color: $text-color; 31 | 32 | // Inputs 33 | $input-border: lighten($text-color, 40%); 34 | $input-border-focus: lighten($brand-primary, 25%); 35 | $input-color-placeholder: lighten($text-color, 30%); 36 | 37 | // Panels 38 | $panel-default-heading-bg: #fff; 39 | -------------------------------------------------------------------------------- /resources/assets/sass/app.scss: -------------------------------------------------------------------------------- 1 | 2 | // Fonts 3 | @import url(https://fonts.googleapis.com/css?family=Raleway:300,400,600); 4 | 5 | // Variables 6 | @import "variables"; 7 | 8 | // Bootstrap 9 | @import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap"; 10 | -------------------------------------------------------------------------------- /resources/assets/sass/blueprintdocs.scss: -------------------------------------------------------------------------------- 1 | // Highlight.js theme 2 | @import "highlight"; 3 | 4 | pre { 5 | border: 1px solid #e6e6e6; 6 | } 7 | 8 | .sidebar { 9 | display: none; 10 | float: left; 11 | margin: 0 15px; 12 | will-change: min-height; 13 | 14 | @media (min-width: 992px) { 15 | display: block; 16 | width: 293px; 17 | } 18 | 19 | @media (min-width: 1200px) { 20 | display: block; 21 | width: 360px; 22 | } 23 | } 24 | 25 | .sidebar__inner { 26 | position: relative; 27 | transform: translate(0, 0); /* For browsers don't support translate3d. */ 28 | transform: translate3d(0, 0, 0); 29 | will-change: position, transform; 30 | overflow: auto; 31 | } 32 | 33 | .host { 34 | margin-bottom: 22px; 35 | text-align: center; 36 | word-wrap: break-word; 37 | } 38 | 39 | .main { 40 | margin-left: 0; 41 | padding: 0 15px; 42 | 43 | @media (min-width: 992px) { 44 | margin-left: 323px; 45 | } 46 | 47 | @media (min-width: 1200px) { 48 | margin-left: 390px; 49 | } 50 | } 51 | 52 | .panel { 53 | border: 1px solid #d3e0e9; 54 | overflow: hidden; 55 | } 56 | 57 | .panel-group .panel { 58 | margin-bottom: 22px; 59 | border-radius: 4px; 60 | } 61 | 62 | .panel-collapsable .panel-heading { 63 | padding: 10px 15px; 64 | color: #333333; 65 | border-color: #d3e0e9; 66 | border-bottom: 1px solid transparent; 67 | border-top-right-radius: 3px; 68 | border-top-left-radius: 3px; 69 | background-color: #f5f5f5; 70 | } 71 | 72 | .panel-collapsable .panel-title { 73 | font-size: 15px; 74 | line-height: 1.6; 75 | } 76 | 77 | .panel-collapsable .panel-title a { 78 | position: relative; 79 | } 80 | 81 | .panel-collapsable .panel-title a:active, 82 | .panel-collapsable .panel-title a:hover, 83 | .panel-collapsable .panel-title a:link, 84 | .panel-collapsable .panel-title a:visited { 85 | text-decoration: none; 86 | } 87 | 88 | .panel-collapsable .panel-title a:after { 89 | content: "-"; 90 | position: absolute; 91 | top: 0; 92 | right: 0; 93 | font-size: 25px; 94 | font-weight: 200; 95 | line-height: .8; 96 | } 97 | 98 | .panel-collapsable .panel-title a.collapsed:after { 99 | content: "+"; 100 | line-height: 1; 101 | } 102 | 103 | .panel-collapsable .panel-body { 104 | padding: 0; 105 | } 106 | 107 | .panel-body { 108 | font-weight: 300; 109 | } 110 | 111 | .stacked-tabs { 112 | border-radius: 4px; 113 | font-weight: 300; 114 | } 115 | 116 | .stacked-tabs a { 117 | border-top: 1px solid #e4ecf2; 118 | border-left: 2px solid transparent; 119 | color: #636b6f; 120 | } 121 | 122 | .stacked-tabs li a:active, 123 | .stacked-tabs li a:hover, 124 | .stacked-tabs li a:link, 125 | .stacked-tabs li a:visited { 126 | background-color: white; 127 | } 128 | 129 | .stacked-tabs li a:hover { 130 | border-left-color: #e4ecf2; 131 | } 132 | 133 | .stacked-tabs ul li a { 134 | padding-left: 30px; 135 | } 136 | 137 | .stacked-tabs li .method { 138 | display: inline-block; 139 | float: right; 140 | } 141 | 142 | .panel-description .panel-title { 143 | font-size: 36px; 144 | line-height: 1.8; 145 | color: #636b6f; 146 | } 147 | 148 | .panel-resource-group > .panel-heading .panel-title { 149 | font-size: 18px; 150 | line-height: 1.6; 151 | } 152 | 153 | .panel-description h2 { 154 | margin-top: 30px; 155 | font-size: 23px; 156 | } 157 | 158 | .panel-description h3 { 159 | margin-top: 30px; 160 | font-size: 20px; 161 | } 162 | 163 | .panel-description h4 { 164 | font-size: 16px; 165 | font-weight: bold; 166 | margin-top: 30px; 167 | } 168 | 169 | .panel-default > .panel-heading { 170 | color: inherit; 171 | } 172 | 173 | .panel-action { 174 | .panel-title { 175 | font-size: 15px; 176 | line-height: 1.6; 177 | 178 | .name { 179 | float: right; 180 | padding: 3px 0; 181 | } 182 | 183 | .method { 184 | display: inline-block; 185 | margin-right: 12px; 186 | padding: 3px 12px; 187 | font-size: 14px; 188 | font-weight: 600; 189 | border-radius: 3px; 190 | 191 | &.get, &.head, &.options { 192 | color: #fff; 193 | background-color: #337ab7; 194 | } 195 | 196 | &.put, &.patch { 197 | color: #fff; 198 | background-color: #ed9c28; 199 | } 200 | 201 | &.post { 202 | color: #fff; 203 | background-color: #5cb85c; 204 | } 205 | 206 | &.delete { 207 | color: #fff; 208 | background-color: #d9534f; 209 | } 210 | } 211 | } 212 | 213 | .definition { 214 | margin: 0 0 11px; 215 | 216 | .uri { 217 | color: #000; 218 | 219 | .hostname { 220 | color: #636b6f; 221 | } 222 | } 223 | } 224 | 225 | .panel-footer { 226 | background-color: #fff; 227 | 228 | .nav-pills > li.active > a, 229 | //.nav-pills>li.active>a:focus, 230 | .nav-pills > li.active > a:hover { 231 | color: #216a94; 232 | background-color: #f5f5f5; 233 | } 234 | 235 | .tab-pane.active > :first-child { 236 | margin-top: 15px; 237 | } 238 | } 239 | } 240 | 241 | .main ul li p { 242 | margin: 0; 243 | } 244 | 245 | .main table { 246 | margin: 0 0 11px; 247 | } 248 | 249 | .main table th, 250 | .main table td { 251 | padding: 6px 12px; 252 | vertical-align: top; 253 | border: 1px solid #e6e6e6; 254 | } 255 | 256 | .panel-collapsable .panel-title a:after { 257 | content: "-"; 258 | position: absolute; 259 | top: 0; 260 | right: 0; 261 | font-size: 25px; 262 | font-weight: 200; 263 | line-height: .8; 264 | } 265 | 266 | .main .parameters table { 267 | width: 100%; 268 | margin-top: 10px; 269 | 270 | p { 271 | margin: 0; 272 | } 273 | } 274 | 275 | .method { 276 | margin-right: 5px; 277 | font-weight: 600; 278 | 279 | &.get, &.head, &.options { 280 | color: #337ab7; 281 | } 282 | 283 | &.put, &.patch { 284 | color: #ed9c28; 285 | } 286 | 287 | &.post { 288 | color: #5cb85c; 289 | } 290 | 291 | &.delete { 292 | color: #d9534f; 293 | } 294 | } -------------------------------------------------------------------------------- /resources/views/action.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | {{ $action->method }} 5 | {!! urldecode($action->uriTemplate) !!} 6 | {{ $action->name }} 7 |

8 |
9 |
10 | {!! $action->descriptionHtml !!} 11 | 12 |

Example URI

13 |
14 | {{ $action->method }} 15 | 16 | {{ $api->host }}{!! urldecode($action->colorizedUriTemplate) !!} 17 | 18 |
19 | 20 | @if ($action->parameters->count()) 21 | @include ('blueprintdocs::parameters') 22 | @endif 23 |
24 | @if ($action->examples->count()) 25 | 51 | @endif 52 |
53 | -------------------------------------------------------------------------------- /resources/views/index.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{ config('app.name', 'Laravel') }} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 |
26 | 35 | 36 |
37 |
38 | 39 | 40 | 52 | 53 | 54 |
55 | 56 | 57 |
58 |
59 |

{{ $api->name }}

60 |
61 |
62 | {!! $api->descriptionHtml !!} 63 |
64 |
65 | 66 | 67 | @foreach($api->resourceGroups as $resourceGroup) 68 | @include('blueprintdocs::resource_group') 69 | @endforeach 70 |
71 |
72 |
73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /resources/views/navigation.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 10 | 11 |
12 |
13 | 22 |
23 |
24 |
25 | 26 | @foreach ($api->resourceGroups as $resourceGroup) 27 |
28 | 35 | 36 |
37 |
38 | 71 |
72 |
73 |
74 | @endforeach 75 |
76 | -------------------------------------------------------------------------------- /resources/views/parameters.blade.php: -------------------------------------------------------------------------------- 1 |

2 | URI Parameters 3 |

4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | @foreach ($action->parameters as $parameter) 14 | 15 | 18 | 41 | 42 | @endforeach 43 | 44 |
16 | {{ $parameter->name }} 17 | 19 |

20 | {{$parameter->type}} 21 | @if ($parameter->required) 22 |  ({{ $parameter->required }}) 23 | @endif 24 | @if ($parameter->defaultValue) 25 |  Default: {{ $parameter->defaultValue }} 26 | @endif 27 | @if ($parameter->example) 28 |  Example: {{ urldecode($parameter->example) }} 29 | @endif 30 |

31 | {!! $parameter->descriptionHtml !!} 32 | @if (!empty($parameter->values)) 33 |

34 | Choices:  35 | @foreach($parameter->values as $value) 36 | {{ $value }} 37 | @endforeach 38 |

39 | @endif 40 |
45 |
46 | -------------------------------------------------------------------------------- /resources/views/requestresponsebody.blade.php: -------------------------------------------------------------------------------- 1 | @if (isset($requestresponse->title)) 2 |

{{ $requestresponse->title }}

3 | @endif 4 | @if (isset($requestresponse->description)) 5 |

Description

6 |

{{ $requestresponse->description }}

7 | @endif 8 | @if ($requestresponse->headers->count()) 9 |

Headers

10 |
@foreach($requestresponse->headers as $key => $value){{ $key . ': ' . $value }}
@endforeach
11 | @endif 12 | @if ($requestresponse->messageBody) 13 |

Body

14 |
{{ $requestresponse->messageBody->body }}
15 | @endif 16 | @if ($requestresponse->messageBodySchema) 17 |

Schema

18 |
{{ $requestresponse->messageBodySchema->body }}
19 | @endif 20 | @if (! $requestresponse->hasContent) 21 |

This has no content.

22 | @endif 23 | -------------------------------------------------------------------------------- /resources/views/resource.blade.php: -------------------------------------------------------------------------------- 1 |

2 | {{ $resource->name ?: 'Resource' }} 3 |

4 | {!! $resource->descriptionHtml !!} 5 | 6 | @foreach($resource->actions as $action) 7 | @include('blueprintdocs::action') 8 | @endforeach -------------------------------------------------------------------------------- /resources/views/resource_group.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | {{ $resourceGroup->name }} 5 |

6 |
7 |
8 | {!! $resourceGroup->descriptionHtml !!} 9 | 10 | @foreach($resourceGroup->resources as $resource) 11 | @include('blueprintdocs::resource') 12 | @endforeach 13 |
14 |
-------------------------------------------------------------------------------- /src/BlueprintDocs.php: -------------------------------------------------------------------------------- 1 | drafter = $drafter; 44 | $this->refractParser = new RefractParser(); 45 | } 46 | 47 | /** 48 | * Parse API Blueprint file 49 | * 50 | * @param string $apib_file 51 | * 52 | * @return BlueprintDocs 53 | */ 54 | public function parse($apib_file) 55 | { 56 | $refract = $this->drafter 57 | ->input($apib_file) 58 | ->format('json') 59 | ->run(); 60 | 61 | $this->refract = json_decode($refract, true); 62 | $this->reynaldo = $this->parseRefract($this->refract); 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * Parse Reynaldo's traversable PHP data structure 69 | * 70 | * @param $refract 71 | * 72 | * @return \Hmaus\Reynaldo\Elements\ApiDescriptionRoot 73 | */ 74 | private function parseRefract($refract) 75 | { 76 | return $this->refractParser 77 | ->parse($refract) 78 | ->getApi(); 79 | } 80 | 81 | /** 82 | * Get BlueprintDocs api object for rendering 83 | * 84 | * @return \M165437\Elements\Api 85 | */ 86 | public function getApi() 87 | { 88 | return new Api($this->reynaldo); 89 | } 90 | 91 | /** 92 | * Get API Elements JSON representation 93 | * 94 | * @return array 95 | */ 96 | public function getRefract() 97 | { 98 | return $this->refract; 99 | } 100 | 101 | /** 102 | * Get Reynaldo's traversable PHP data structure 103 | * 104 | * @return \Hmaus\Reynaldo\Elements\ApiDescriptionRoot 105 | */ 106 | public function getReynaldo() 107 | { 108 | return $this->reynaldo; 109 | } 110 | } -------------------------------------------------------------------------------- /src/BlueprintDocsController.php: -------------------------------------------------------------------------------- 1 | parse(config('blueprintdocs.blueprint_file')) 20 | ->getApi(); 21 | 22 | return view('blueprintdocs::index', compact('api')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlueprintDocsServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../resources/views', 'blueprintdocs'); 18 | 19 | if (config('blueprintdocs.route')) { 20 | $this->loadRoutesFrom(__DIR__ . '/routes.php'); 21 | } 22 | 23 | $this->definePublishes(); 24 | } 25 | 26 | /** 27 | * Define publishable files. 28 | * 29 | * @return void 30 | */ 31 | private function definePublishes() 32 | { 33 | $this->publishes([ 34 | __DIR__.'/../config/blueprintdocs.php' => config_path('blueprintdocs.php'), 35 | ], 'config'); 36 | 37 | $this->publishes([ 38 | __DIR__.'/../public' => public_path('vendor/blueprintdocs'), 39 | ], 'public'); 40 | 41 | $this->publishes([ 42 | __DIR__.'/../resources/views' => resource_path('views/vendor/blueprintdocs'), 43 | ], 'views'); 44 | 45 | $this->publishes([ 46 | __DIR__.'/../example.apib' => base_path('blueprint.apib'), 47 | ], 'example'); 48 | } 49 | 50 | /** 51 | * Register the package services. 52 | * 53 | * @return void 54 | */ 55 | public function register() 56 | { 57 | $this->registerDrafter(); 58 | 59 | $this->registerParsedown(); 60 | 61 | $this->registerConfiguration(); 62 | } 63 | 64 | /** 65 | * Register Drafter. 66 | * 67 | * @return void 68 | */ 69 | private function registerDrafter() 70 | { 71 | $this->app->singleton(Drafter::class, function () { 72 | $drafterBinary = config('blueprintdocs.drafter'); 73 | return new Drafter($drafterBinary); 74 | }); 75 | } 76 | 77 | /** 78 | * Register Parsedown. 79 | * 80 | * @return void 81 | */ 82 | private function registerParsedown() 83 | { 84 | $this->app->singleton(Parsedown::class); 85 | } 86 | 87 | /** 88 | * Register configuration 89 | * 90 | * @return void 91 | */ 92 | public function registerConfiguration() 93 | { 94 | $this->mergeConfigFrom( 95 | __DIR__.'/../config/blueprintdocs.php', 'blueprintdocs' 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/DescriptionParser.php: -------------------------------------------------------------------------------- 1 | headings = []; 30 | $this->markup = parent::text($text); 31 | 32 | return $this; 33 | } 34 | 35 | /** 36 | * Get HTML markup from parsed markdown. 37 | * 38 | * @return string 39 | */ 40 | public function getCopyText() 41 | { 42 | return $this->markup; 43 | } 44 | 45 | /** 46 | * Get headings from parsed markdown. 47 | * 48 | * @return array 49 | */ 50 | public function getHeadings() 51 | { 52 | return $this->headings; 53 | } 54 | 55 | /** 56 | * Handle markdown elements. 57 | * 58 | * @param array $Element 59 | * 60 | * @return string 61 | */ 62 | protected function element(array $Element) 63 | { 64 | if ($this->isHeader($Element)) { 65 | $Element['attributes']['id'] = sprintf( 66 | 'description-%s-%d', 67 | Str::slug($Element['text'], '-'), 68 | count($this->headings) 69 | ); 70 | $Element['level'] = $Element['name'][1]; 71 | array_push($this->headings, $Element); 72 | } 73 | 74 | return parent::element($Element); 75 | } 76 | 77 | /** 78 | * Evaluate header tag. 79 | * 80 | * @param array $Element 81 | * 82 | * @return boolean 83 | */ 84 | protected function isHeader(array $Element) 85 | { 86 | return preg_match('/^[hH][1-6]$/', $Element['name']) ? true : false; 87 | } 88 | } -------------------------------------------------------------------------------- /src/Elements/Action.php: -------------------------------------------------------------------------------- 1 | method = $this->mapMethod(); 53 | $this->methodLower = strtolower($this->method); 54 | $this->parameters = $this->mapParameters(); 55 | $this->examples = $this->mapExamples(); 56 | $this->uriTemplate = $this->mapUriTemplate(); 57 | $this->colorizedUriTemplate = $this->mapColorizedUriTemplate(); 58 | } 59 | 60 | /** 61 | * Map method 62 | * 63 | * @return \Illuminate\Support\Collection 64 | */ 65 | private function mapMethod() 66 | { 67 | return $this->reynaldo->getHttpTransactions()[0]->getHttpRequest()->getMethod(); 68 | } 69 | 70 | /** 71 | * Map parameters 72 | * 73 | * @return \Illuminate\Support\Collection 74 | */ 75 | private function mapParameters() 76 | { 77 | $resourceParams = new Collection( 78 | $this->parent->reynaldo->getHrefVariablesElement()->getAllVariables() 79 | ); 80 | 81 | $actionParams = new Collection( 82 | $this->reynaldo->getHrefVariablesElement()->getAllVariables() 83 | ); 84 | 85 | return $resourceParams->keyBy('name') 86 | ->merge($actionParams->keyBy('name')) 87 | ->flatten(1) 88 | ->map(function($reynaldoHrefVariablesElement) { 89 | return new HrefVariable($reynaldoHrefVariablesElement, $this); 90 | }); 91 | } 92 | 93 | /** 94 | * Map request and response examples 95 | * 96 | * @return \Illuminate\Support\Collection 97 | */ 98 | private function mapExamples() 99 | { 100 | return (new Collection($this->reynaldo->getHttpTransactions())) 101 | ->map(function ($reynaldoTransaction) { 102 | return new Collection([ 103 | 'request' => new HttpRequest($reynaldoTransaction->getHttpRequest(), $this), 104 | 'response' => new HttpResponse($reynaldoTransaction->getHttpResponse(), $this) 105 | ]); 106 | }); 107 | } 108 | 109 | /** 110 | * Map URI template 111 | * 112 | * @return string 113 | */ 114 | private function mapUriTemplate() 115 | { 116 | return $this->modifyUriTemplate($this->parent->reynaldo->getHref(), $this->parameters, false); 117 | } 118 | 119 | /** 120 | * Map colorized URI template 121 | * 122 | * @return string 123 | */ 124 | private function mapColorizedUriTemplate() 125 | { 126 | return $this->modifyUriTemplate($this->parent->reynaldo->getHref(), $this->parameters, true); 127 | } 128 | 129 | /** 130 | * Modify URI template 131 | * 132 | * @param $templateUri 133 | * @param $parameters 134 | * @param $colorize 135 | * 136 | * @return string 137 | */ 138 | private function modifyUriTemplate($templateUri, $parameters, $colorize) 139 | { 140 | $parameterNames = (new Collection($parameters))->pluck('name')->toArray(); 141 | $parameterBlocks = []; 142 | $lastIndex = $index = 0; 143 | while ($index = strpos($templateUri, '{', $index)) { 144 | $parameterBlocks[] = substr($templateUri, $lastIndex, $index - $lastIndex); 145 | $closeIndex = strpos($templateUri, '}', $index); 146 | $block = [ 147 | 'querySet' => (strpos($templateUri, '{?', $index) === $index), 148 | 'formSet' => (strpos($templateUri, '{&', $index) === $index), 149 | 'reservedSet' => (strpos($templateUri, '{+', $index) === $index), 150 | ]; 151 | $lastIndex = $closeIndex + 1; 152 | $index++; 153 | if (in_array(true, $block)) { 154 | $index++; 155 | } 156 | $parameterSet = substr($templateUri, $index, $closeIndex - $index); 157 | $block['parameters'] = array_filter(explode(',', $parameterSet), function($val) use ($parameterNames) { 158 | return array_search(urldecode(preg_replace('/^\*|\*$/', '', $val)), $parameterNames) !== false; 159 | }); 160 | if (count($block['parameters'])) { 161 | $parameterBlocks[] = $block; 162 | } 163 | } 164 | $parameterBlocks[] = substr($templateUri, $lastIndex); 165 | return preg_replace('/\/+/', '/', array_reduce( 166 | array_keys($parameterBlocks), 167 | function($uri, $key) use ($parameters, $colorize, $parameterBlocks, $parameterNames) { 168 | $block = $parameterBlocks[$key]; 169 | if (is_string($block)) { 170 | $uri .= $block; 171 | } else { 172 | $segment = !$colorize ? ['{'] : []; 173 | if ($block['querySet']) { 174 | $segment[] = '?'; 175 | } 176 | if ($block['formSet']) { 177 | $segment[] = '&'; 178 | } 179 | if ($block['reservedSet'] && !$colorize) { 180 | $segment[] = '+'; 181 | } 182 | $segment[] = implode($colorize ? '&' : ',', array_map( 183 | function($name) use ($block, $parameters, $colorize, $parameterNames) { 184 | if (!$colorize) { 185 | return $name; 186 | } else { 187 | $name = preg_replace('/^\*|\*$/', '', $name); 188 | $param = $parameters[array_search(urldecode($name), $parameterNames)]; 189 | if ($block['querySet'] || $block['formSet']) { 190 | $example = isset($param->example) ? is_bool($param->example) ? $param->example ? 'true' : 'false' : $param->example : ''; 191 | return '' . $name . '=' . '' . $example . ''; 192 | } else { 193 | $example = isset($param->example) ? is_bool($param->example) ? $param->example ? 'true' : 'false' : $param->example : $name; 194 | return '' . $example . ''; 195 | } 196 | } 197 | }, 198 | $block['parameters'] 199 | )); 200 | if (!$colorize) { 201 | $segment[] = '}'; 202 | } 203 | $uri .= implode($segment); 204 | } 205 | return $uri; 206 | } 207 | )); 208 | } 209 | } -------------------------------------------------------------------------------- /src/Elements/Api.php: -------------------------------------------------------------------------------- 1 | reynaldo = $reynaldoApiDescriptionRoot; 58 | $this->setup(); 59 | } 60 | 61 | /** 62 | * Setup element variables 63 | * 64 | * @return void 65 | */ 66 | protected function setup() 67 | { 68 | $this->name = $this->reynaldo->getTitle(); 69 | $this->meta = collect($this->reynaldo->getApiMetaData()); 70 | $this->format = !empty($this->meta['FORMAT']) ? $this->meta['FORMAT'] : ''; 71 | $this->host = !empty($this->meta['HOST']) ? $this->meta['HOST'] : ''; 72 | $this->resourceGroups = $this->mapResourceGroups(); 73 | $this->parseDescription(); 74 | } 75 | 76 | /** 77 | * Parse description to markup and get headings 78 | * 79 | * @return void 80 | */ 81 | private function parseDescription() 82 | { 83 | $descriptionParser = new DescriptionParser(); 84 | $descriptionParser->parse($this->reynaldo->getApiDocumentDescription()); 85 | 86 | $this->descriptionHtml = $descriptionParser->getCopyText(); 87 | $this->descriptionHeadings = collect($descriptionParser->getHeadings()) 88 | ->map(function ($heading) { 89 | return (object) [ 90 | 'id' => $heading['attributes']['id'], 91 | 'name' => $heading['name'], 92 | 'text' => $heading['text'], 93 | 'level' => $heading['level'] 94 | ]; 95 | })->values(); 96 | } 97 | 98 | /** 99 | * Map resource Groups 100 | * 101 | * @return \Illuminate\Support\Collection 102 | */ 103 | private function mapResourceGroups() 104 | { 105 | return collect($this->reynaldo->getResourceGroups())->map(function ($reynaldoResourceGroup) { 106 | return new ResourceGroup($reynaldoResourceGroup); 107 | }); 108 | } 109 | } -------------------------------------------------------------------------------- /src/Elements/Asset.php: -------------------------------------------------------------------------------- 1 | setup(); 27 | } 28 | 29 | /** 30 | * Setup element variables 31 | * 32 | * @return void 33 | */ 34 | public function setup() 35 | { 36 | $this->contentType = $this->reynaldo->getContentType(); 37 | $this->body = $this->reynaldo->getBody(); 38 | } 39 | } -------------------------------------------------------------------------------- /src/Elements/Base.php: -------------------------------------------------------------------------------- 1 | setup(); 39 | } 40 | 41 | /** 42 | * Setup element variables 43 | * 44 | * @return void 45 | */ 46 | protected function setup() 47 | { 48 | $this->name = $this->reynaldo->getTitle(); 49 | $this->elementId = $this->mapElementId(); 50 | $this->elementLink = '#' . $this->elementId; 51 | $this->descriptionHtml = resolve('Parsedown')->parse($this->reynaldo->getCopyText()); 52 | } 53 | 54 | /** 55 | * Map element id 56 | * 57 | * @return string 58 | */ 59 | private function mapElementId() 60 | { 61 | $class = $this; 62 | $elementId = Str::slug($this->name, '-'); 63 | 64 | while ($parent = $class->parent) { 65 | $elementId = Str::slug($parent->name, '-') . '-' . $elementId; 66 | $class = $parent; 67 | } 68 | 69 | return $elementId; 70 | } 71 | } -------------------------------------------------------------------------------- /src/Elements/HrefVariable.php: -------------------------------------------------------------------------------- 1 | setup(); 57 | } 58 | 59 | /** 60 | * Setup element variables 61 | * 62 | * @return void 63 | */ 64 | public function setup() 65 | { 66 | $this->name = $this->reynaldo->name; 67 | $this->description = $this->reynaldo->description; 68 | $this->descriptionHtml = resolve('Parsedown')->parse($this->reynaldo->description); 69 | $this->type = $this->reynaldo->dataType; 70 | $this->required = $this->reynaldo->required; 71 | $this->example = $this->reynaldo->example; 72 | $this->defaultValue = $this->mapDefault($this->reynaldo->default); 73 | $this->values = $this->reynaldo->values; 74 | } 75 | 76 | /** 77 | * Map default value to string 78 | * 79 | * @param mixed $default 80 | * @return string 81 | */ 82 | private function mapDefault($default) 83 | { 84 | return is_array($default) ? $default[0]['content'] : $default; 85 | } 86 | } -------------------------------------------------------------------------------- /src/Elements/HttpRequest.php: -------------------------------------------------------------------------------- 1 | setup(); 59 | } 60 | 61 | /** 62 | * Setup element variables 63 | * 64 | * @return void 65 | */ 66 | public function setup() 67 | { 68 | $this->method = $this->reynaldo->getMethod(); 69 | $this->headers = new Collection($this->reynaldo->getHeaders()); 70 | $this->contentType = $this->reynaldo->getContentType(); 71 | $this->messageBody = $this->mapMessageBody(); 72 | $this->messageBodySchema = $this->mapMessageBodySchema(); 73 | $this->hasContent = $this->headers->count() || $this->messageBody || $this->messageBodySchema; 74 | $this->title = $this->reynaldo->getTitle(); 75 | $this->description = $this->reynaldo->getCopyText(); 76 | } 77 | 78 | /** 79 | * Map message body 80 | * 81 | * @return mixed 82 | */ 83 | private function mapMessageBody() 84 | { 85 | return $this->reynaldo->hasMessageBody() ? 86 | new Asset($this->reynaldo->getMessageBodyAsset(), $this) : 87 | null; 88 | } 89 | 90 | /** 91 | * Map message body schema 92 | * 93 | * @return mixed 94 | */ 95 | private function mapMessageBodySchema() 96 | { 97 | return $this->reynaldo->hasMessageBodySchema() ? 98 | new Asset($this->reynaldo->getMessageBodySchemaAsset(), $this) : 99 | null; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Elements/HttpResponse.php: -------------------------------------------------------------------------------- 1 | setup(); 54 | } 55 | 56 | /** 57 | * Setup element variables 58 | * 59 | * @return void 60 | */ 61 | public function setup() 62 | { 63 | $this->statusCode = $this->reynaldo->getStatusCode(); 64 | $this->headers = new Collection($this->reynaldo->getHeaders()); 65 | $this->messageBody = $this->mapMessageBody(); 66 | $this->messageBodySchema = $this->mapMessageBodySchema(); 67 | $this->dataStructure = $this->reynaldo->getDataStructure(); 68 | $this->hasContent = $this->headers->count() || $this->messageBody || $this->messageBodySchema; 69 | $this->description = $this->reynaldo->getCopyText(); 70 | } 71 | 72 | /** 73 | * Map message body 74 | * 75 | * @return mixed 76 | */ 77 | private function mapMessageBody() 78 | { 79 | return $this->reynaldo->hasMessageBody() ? 80 | new Asset($this->reynaldo->getMessageBodyAsset(), $this) : 81 | null; 82 | } 83 | 84 | /** 85 | * Map message body schema 86 | * 87 | * @return mixed 88 | */ 89 | private function mapMessageBodySchema() 90 | { 91 | return $this->reynaldo->hasMessageBodySchema() ? 92 | new Asset($this->reynaldo->getMessageBodySchemaAsset(), $this) : 93 | null; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Elements/Mapping.php: -------------------------------------------------------------------------------- 1 | reynaldo = $element; 29 | $this->parent = $parent; 30 | } 31 | 32 | /** 33 | * Find parent element 34 | * 35 | * @param string $parentClassName 36 | * 37 | * @return mixed 38 | * @throws Exception 39 | */ 40 | protected function findParent(string $parentClassName) 41 | { 42 | $class = $this; 43 | 44 | while ($parent = $class->parent) { 45 | if ((new ReflectionClass($parent))->getShortName() === $parentClassName) { 46 | return $parent; 47 | } 48 | 49 | $class = $parent; 50 | } 51 | 52 | throw new Exception('Couldn\'t find desired parent.'); 53 | } 54 | } -------------------------------------------------------------------------------- /src/Elements/Resource.php: -------------------------------------------------------------------------------- 1 | actions = $this->mapActions(); 21 | } 22 | 23 | /** 24 | * Map actions 25 | * 26 | * @return \Illuminate\Support\Collection 27 | */ 28 | private function mapActions() 29 | { 30 | return collect($this->reynaldo->getTransitions()) 31 | ->map(function ($reynaldoTransition) { 32 | return new Action($reynaldoTransition, $this); 33 | }); 34 | } 35 | } -------------------------------------------------------------------------------- /src/Elements/ResourceGroup.php: -------------------------------------------------------------------------------- 1 | resources = $this->mapRecources(); 21 | } 22 | 23 | /** 24 | * Map resources 25 | * 26 | * @return \Illuminate\Support\Collection 27 | */ 28 | private function mapRecources() 29 | { 30 | return collect($this->reynaldo->getResources()) 31 | ->map(function ($reynaldoResource) { 32 | return new Resource($reynaldoResource, $this); 33 | }); 34 | } 35 | } -------------------------------------------------------------------------------- /src/routes.php: -------------------------------------------------------------------------------- 1 |