├── .gitignore ├── composer.json ├── license.md ├── phpunit.xml ├── readme.md └── src ├── LaravelSessionStore.php ├── Livewire ├── FlashContainer.php ├── FlashMessage.php └── FlashOverlay.php ├── LivewireFlash.php ├── LivewireFlashNotifier.php ├── LivewireFlashServiceProvider.php ├── Message.php ├── OverlayMessage.php ├── SessionStore.php ├── functions.php ├── publish └── livewire-flash.php └── views └── livewire ├── flash-container.blade.php ├── flash-message.blade.php └── flash-overlay.blade.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mattlibera/livewire-flash", 3 | "description": "Flash notifications using Livewire", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Matt Libera", 8 | "email": "me@mattlibera.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=7.3.0", 13 | "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", 14 | "livewire/livewire": "^1.2|^2.12.7|^3.5.2" 15 | }, 16 | "require-dev": { 17 | "mockery/mockery": "dev-master", 18 | "phpunit/phpunit": "^6.1|^9.5.10|^10.5" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "MattLibera\\LivewireFlash\\": "src/" 23 | }, 24 | "files": [ 25 | "src/functions.php" 26 | ] 27 | }, 28 | "minimum-stability": "stable", 29 | "extra": { 30 | "laravel": { 31 | "providers": [ 32 | "MattLibera\\LivewireFlash\\LivewireFlashServiceProvider" 33 | ], 34 | "aliases": { 35 | "LivewireFlash": "MattLibera\\LivewireFlash\\LivewireFlash" 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 Matt Libera (original work Jeffrey Way) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Livewire Flash 2 | 3 | This package provides flash message capability using Laravel Livewire. It is based very literally on `laracasts/flash` but has been extended to add the ability to flash a message to a the flash container (a Livewire component) without reloading the page. 4 | 5 | This package also retains much (though not all) of the same capability for "normal" flash messages, which are displayed on refresh by the same Livewire component. 6 | 7 | ## Installation 8 | 9 | Install via composer: 10 | 11 | ```bash 12 | composer require mattlibera/livewire-flash 13 | ``` 14 | 15 | ## Requirements 16 | 17 | * Laravel >=7.0 18 | * Livewire ^1.2, ^2.12.7, or ^3.5.2 19 | 20 | > For new applications, consider using the TALL preset for Laravel: [https://github.com/laravel-frontend-presets/tall], or this package also works well with Laravel Jetstream: [https://jetstream.laravel.com/] 21 | 22 | ## Recommended add-ons 23 | 24 | Out of the box, the default alert component uses: 25 | 26 | * TailwindCSS (any version; see below for installation instructions) 27 | * FontAwesome 28 | 29 | However, it's fairly trivial to implement your own views / styles instead, by publishing the config and overriding defaults. See below for more on that. 30 | 31 | ### Tailwind setup (4.0+) 32 | 33 | If using modern Tailwind CSS, you should add a couple of paths to your `app.css` as `@source` directives: 34 | 35 | ```css 36 | @source "../../vendor/mattlibera/livewire-flash/src/publish/livewire-flash.php"; 37 | @source "../../vendor/mattlibera/livewire-flash/src/views/livewire/*.blade.php"; 38 | ``` 39 | Of course, if you publish the views/config, you'll reference your own copies instead. 40 | 41 | ### Tailwind setup (> 4.0) 42 | 43 | If you are using legacy Tailwind CSS, you should amend the `content` section of your `tailwind.config.js` to include the appropriate files from this package: 44 | ```js 45 | /** @type {import('tailwindcss').Config} */ 46 | module.exports = { 47 | content: [ 48 | "./resources/**/*.blade.php", 49 | "./resources/**/*.js", 50 | "./resources/**/*.vue", 51 | "./vendor/mattlibera/livewire-flash/src/publish/livewire-flash.php", 52 | "./vendor/mattlibera/livewire-flash/src/views/livewire/*.blade.php", 53 | ], 54 | theme: { 55 | extend: {}, 56 | }, 57 | plugins: [], 58 | } 59 | ``` 60 | Of course, if you publish the views/config, you'll reference your own copies instead. 61 | 62 | ## Usage 63 | 64 | ### Normal flash messages (on reload) 65 | 66 | Call the `flash()` helper from your code somewhere, before you redirect. 67 | 68 | ```php 69 | public function store() 70 | { 71 | flash('Success!'); 72 | 73 | return redirect()->back(); 74 | } 75 | ``` 76 | 77 | ### Livewire flash message (before reload) 78 | 79 | From your Livewire component, flash your message using the normal syntax, but then call the `livewire()` helper method as the last method in the chain. You must pass in `$this` as the argument, as this package utilizes the `emit` helper that exists on all Livewire components. Example: 80 | 81 | ```php 82 | public function livewireAction() 83 | { 84 | flash('Your request was successful!')->success()->livewire($this); 85 | } 86 | ``` 87 | 88 | ### Message types 89 | 90 | Message types are defined in the `livewire-flash.php` config file, which can be published (see below) if desired. By default, there are four supported message types: `info` (default if nothing else is specified), `success`, `warning`, and `error`. 91 | 92 | To set a message's type, either: 93 | 94 | 1. Pass it as the second argument to `flash()` - example: `flash('Your action succeeded', 'success')`, or 95 | 2. Chain it as a method name fluently after `flash()` - example: `flash('Your action succeeded')->success()` 96 | 97 | Both of those will change the message's display (colors and icon) to the configured styles. 98 | 99 | ### Overlay Message 100 | 101 | Overlay message is defined in the `livewire-flash.php` config file, which can be published (see below) if desired. 102 | 103 | To set an overlay message, chain the method name `overlay()` after `flash()`. When using overlay leave the `flash()` parameter empty. Enter your message as the first parameter and title as second parameter for `overlay()`. This can be used with or without the `livewire($this)` suffix: 104 | 105 | ``` 106 | // renders on next page load 107 | flash()->overlay('This is my message', 'The Title'); 108 | return redirect('somewhere'); 109 | 110 | // renders immediately via Livewire 111 | flash()->overlay('This is my message', 'The Title')->livewire($this); 112 | ``` 113 | 114 | Note that the out-of-the-box overlay component does support HTML code for the body and title, using the Blade unescaped `{!! !!}` tags. 115 | 116 | ### Customization 117 | 118 | To change the styles used by each message type, OR to add your own types, first publish the config file: 119 | 120 | ```bash 121 | php artisan vendor:publish --provider="MattLibera\LivewireFlash\LivewireFlashServiceProvider" 122 | ``` 123 | 124 | Then, in the `styles` key you can change whatever you want: 125 | 126 | ```php 127 | 'styles' => [ 128 | 'info' => [ 129 | 'bg-color' => 'bg-blue-100', // could change to bg-purple-100, or something. 130 | 'border-color' => 'border-blue-400', 131 | 'icon-color' => 'text-blue-400', 132 | 'text-color' => 'text-blue-800', 133 | 'icon' => 'fas fa-info-circle', // could change to another FontAwesome icon 134 | ], 135 | ``` 136 | 137 | Or you can add your own: 138 | 139 | ```php 140 | 'notice' => [ 141 | 'bg-color' => 'bg-orange-100', 142 | 'border-color' => 'border-orange-400', 143 | 'icon-color' => 'text-orange-400', 144 | 'text-color' => 'text-orange-800', 145 | 'icon' => 'fas fa-flag', 146 | ], 147 | ``` 148 | 149 | Whatever the case, just ensure that you call the alert by its config key: `flash('An important message')->notice()` 150 | 151 | To customize overlay styles, see the `overlay` key of the config file. 152 | 153 | ## Templates 154 | 155 | Out of the box, the Livewire Flash Container component is registered for you. All you have to do is include it in your template: 156 | 157 | ```html 158 | 159 | ``` 160 | 161 | There are also some sample alert components (styled using TailwindCSS) included with this package. However, if you do not wish to use those... 162 | 163 | ### Customization 164 | 165 | You can change the views that the Livewire components use for rendering, and the styles applied to each message type. 166 | 167 | > If you are not using TailwindCSS and/or FontAwesome, you should definitely do this to call your own alert component/partial to fit whatever your stack is using. 168 | 169 | First, publish the config file: 170 | 171 | ```bash 172 | php artisan vendor:publish --provider="MattLibera\LivewireFlash\LivewireFlashServiceProvider" 173 | ``` 174 | 175 | Then, edit the `views` area: 176 | 177 | ```php 178 | 'views' => [ 179 | 'container' => 'livewire-flash::livewire.flash-container', 180 | 'message' => 'partials.my-bootstrap-flash', 181 | ], 182 | ``` 183 | 184 | You can access the public message properties on `MattLibera\LivewireFlash\Message`, as well as `$styles` (which is injected via the Livewire component) in your template. 185 | 186 | ## Dismissible Messages 187 | 188 | By default, each message will be set to be dismissible (that is, have an X icon at the right that will close the alert). If you wish to prevent this, you can chain `->notDismissable()` (or `->dismissable(false)`) to your flash directive. 189 | 190 | You can add your own magic via AlpineJS or whatever else if you want to fade messages out automatically - right now each message is a Livewire component and uses Livewire logic to hide it when it is dismissed. 191 | 192 | _Note that the overlay does not support this directive._ 193 | 194 | ## Multiple Flash Messages 195 | 196 | Multiple flash messages can be sent to the session: 197 | 198 | ```php 199 | // anywhere 200 | flash('Message 1'); 201 | flash('Message 2')->warning(); 202 | 203 | return redirect('somewhere'); 204 | ``` 205 | 206 | OR 207 | 208 | ```php 209 | // livewire component 210 | flash('Message 1')->livewire($this); 211 | flash('Message 2')->warning()->livewire($this); 212 | ``` 213 | 214 | However, at the moment, because of the way Livewire handles the session, you *cannot* mix-and-match... that is, you cannot do: 215 | 216 | ```php 217 | // livewire component 218 | flash('Message 1'); // this one will get lost. 219 | flash('Message 2')->livewire($this); // this one will show on current page via Livewire 220 | 221 | ``` 222 | 223 | # Contributing 224 | 225 | I am open to contributions to this package, and will do the best I can to maintain it over time. Pull requests are welcome, and in fact encouraged. Right now there are no specific guidelines for a PR. 226 | 227 | # Road Map 228 | 229 | Some considerations for future versions: 230 | 231 | - Fluent options for setting an icon or colors on the fly 232 | - Auto-dismissing option for flash messages 233 | 234 | # Credits and License 235 | 236 | Credit for the original package goes to Jeffrey Way and Laracasts. Additional thanks: 237 | 238 | * Caleb Porzio and his Livewire contributors for the awesome framework 239 | * Adam Wathan and the Tailwind crew 240 | * Taylor Otwell and co. for Laravel 241 | 242 | This is an MIT-licensed package. Please read license.md for the details. 243 | -------------------------------------------------------------------------------- /src/LaravelSessionStore.php: -------------------------------------------------------------------------------- 1 | session = $session; 22 | } 23 | 24 | /** 25 | * Flash a message to the session. 26 | * 27 | * @param string $name 28 | * @param array $data 29 | */ 30 | public function flash($name, $data) 31 | { 32 | $this->session->flash($name, $data); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Livewire/FlashContainer.php: -------------------------------------------------------------------------------- 1 | messages = session('flash_notification', collect())->toArray(); 17 | session()->forget('flash_notification'); 18 | } 19 | 20 | public function render() 21 | { 22 | return view(config('livewire-flash.views.container')); 23 | } 24 | 25 | public function flashMessageAdded($message) 26 | { 27 | $this->messages[] = $message; 28 | } 29 | 30 | public function dismissMessage($key) 31 | { 32 | unset($this->messages[$key]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Livewire/FlashMessage.php: -------------------------------------------------------------------------------- 1 | message = $message; 20 | $this->styles = config('livewire-flash.styles.' . $this->message['level']); 21 | } 22 | 23 | public function render() 24 | { 25 | return view(config('livewire-flash.views.message')); 26 | } 27 | 28 | public function dismiss() 29 | { 30 | $this->shown = false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Livewire/FlashOverlay.php: -------------------------------------------------------------------------------- 1 | message = $message; 20 | $this->styles = config('livewire-flash.styles.overlay'); 21 | } 22 | 23 | public function render() 24 | { 25 | return view(config('livewire-flash.views.overlay')); 26 | } 27 | 28 | public function dismiss() 29 | { 30 | $this->shown = false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/LivewireFlash.php: -------------------------------------------------------------------------------- 1 | session = $session; 34 | $this->messages = collect(); 35 | } 36 | 37 | /** 38 | * Flash a general message. 39 | * 40 | * @param string|null $message 41 | * @param string|null $level 42 | * @return $this 43 | */ 44 | public function message($message = null, $level = null) 45 | { 46 | // If no message was provided, we should update 47 | // the most recently added message. 48 | if (! $message) { 49 | return $this->updateLastMessage(compact('level')); 50 | } 51 | 52 | if (! $message instanceof Message) { 53 | $message = new Message(compact('message', 'level')); 54 | } 55 | 56 | $this->messages->push($message); 57 | 58 | return $this->flash(); 59 | } 60 | 61 | /** 62 | * Modify the most recently added message. 63 | * 64 | * @param array $overrides 65 | * @return $this 66 | */ 67 | protected function updateLastMessage($overrides = []) 68 | { 69 | $this->messages->last()->update($overrides); 70 | 71 | return $this; 72 | } 73 | 74 | /** 75 | * Flash an overlay modal. 76 | * 77 | * @param string|null $message 78 | * @param string $title 79 | * @return $this 80 | */ 81 | public function overlay($message = null, $title = null) 82 | { 83 | if (! $message) { 84 | return $this->updateLastMessage(['title' => $title, 'overlay' => true]); 85 | } 86 | 87 | return $this->message( 88 | new OverlayMessage(compact('title', 'message')) 89 | ); 90 | } 91 | 92 | /** 93 | * Add an "important" flash to the session. 94 | * 95 | * @return $this 96 | */ 97 | public function important() 98 | { 99 | return $this->updateLastMessage(['important' => true]); 100 | } 101 | 102 | /** 103 | * Set the dismissability of the last flash message. 104 | * 105 | * @param bool $dismissable 106 | * 107 | * @return $this 108 | */ 109 | public function dismissable(bool $dismissable = true) 110 | { 111 | return $this->updateLastMessage(['dismissable' => $dismissable]); 112 | } 113 | 114 | /** 115 | * Convenience method to set dismissable = false on a message 116 | * 117 | * @return void 118 | */ 119 | public function notDismissable() 120 | { 121 | return $this->dismissable(false); 122 | } 123 | 124 | /** 125 | * Clear all registered messages. 126 | * 127 | * @return $this 128 | */ 129 | public function clear() 130 | { 131 | $this->messages = collect(); 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * Flash all messages to the session. 138 | */ 139 | protected function flash() 140 | { 141 | $this->session->flash('flash_notification', $this->messages); 142 | 143 | return $this; 144 | } 145 | 146 | /** 147 | * Pop the last message off the stack and emit it to the Livewire component 148 | * 149 | * @param Livewire\Component $livewire 150 | * @return \MattLibera\LivewireFlash\LivewireFlashNotifier 151 | */ 152 | public function livewire(Component $livewire) 153 | { 154 | if (method_exists($livewire, 'dispatch')) { 155 | $livewire->dispatch('flashMessageAdded', $this->messages->pop()); 156 | } else { 157 | $livewire->emit('flashMessageAdded', $this->messages->pop()); 158 | } 159 | 160 | return $this; 161 | } 162 | 163 | 164 | /** 165 | * Magic __call: pass the method name called as the message type if it is configured 166 | * 167 | * @param mixed $method 168 | * @param mixed $arguments 169 | * @return \MattLibera\LivewireFlash\LivewireFlashNotifier 170 | */ 171 | public function __call($method, $arguments) 172 | { 173 | $messageTypes = config('livewire-flash.styles'); 174 | if (isset($messageTypes[$method])) { 175 | return $this->message(null, $method); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/LivewireFlashServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->bind( 25 | 'MattLibera\LivewireFlash\SessionStore', 26 | 'MattLibera\LivewireFlash\LaravelSessionStore' 27 | ); 28 | 29 | $this->app->singleton('lwflash', function () { 30 | return $this->app->make('MattLibera\LivewireFlash\LivewireFlashNotifier'); 31 | }); 32 | } 33 | 34 | /** 35 | * Bootstrap the application events. 36 | * 37 | * @return void 38 | */ 39 | public function boot() 40 | { 41 | $this->mergeConfigFrom(__DIR__. '/publish/livewire-flash.php', 'livewire-flash'); 42 | 43 | $this->loadViewsFrom(__DIR__ . '/views', 'livewire-flash'); 44 | 45 | $this->publishes([ 46 | __DIR__ . '/publish' => config_path() 47 | ]); 48 | 49 | Livewire::component('flash-container', \MattLibera\LivewireFlash\Livewire\FlashContainer::class); 50 | Livewire::component('flash-message', \MattLibera\LivewireFlash\Livewire\FlashMessage::class); 51 | Livewire::component('flash-overlay', \MattLibera\LivewireFlash\Livewire\FlashOverlay::class); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Message.php: -------------------------------------------------------------------------------- 1 | update($attributes); 57 | } 58 | 59 | /** 60 | * Update the attributes. 61 | * 62 | * @param array $attributes 63 | * @return $this 64 | */ 65 | public function update($attributes = []) 66 | { 67 | foreach ($attributes as $key => $attribute) { 68 | $this->$key = $attribute; 69 | } 70 | return $this; 71 | } 72 | 73 | 74 | /** 75 | * Whether the given offset exists. 76 | * 77 | * @param mixed $offset 78 | * @return bool 79 | */ 80 | public function offsetExists($offset) 81 | { 82 | return isset($this->$offset); 83 | } 84 | 85 | /** 86 | * Fetch the offset. 87 | * 88 | * @param mixed $offset 89 | * @return mixed 90 | */ 91 | public function offsetGet($offset) 92 | { 93 | return $this->$offset; 94 | } 95 | 96 | /** 97 | * Assign the offset. 98 | * 99 | * @param mixed $offset 100 | * @return void 101 | */ 102 | public function offsetSet($offset, $value) 103 | { 104 | $this->$offset = $value; 105 | } 106 | 107 | /** 108 | * Unset the offset. 109 | * 110 | * @param mixed $offset 111 | * @return void 112 | */ 113 | public function offsetUnset($offset) 114 | { 115 | // 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/OverlayMessage.php: -------------------------------------------------------------------------------- 1 | message($message, $level); 19 | } 20 | 21 | return $notifier; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/publish/livewire-flash.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'container' => 'livewire-flash::livewire.flash-container', 6 | 'message' => 'livewire-flash::livewire.flash-message', 7 | 'overlay' => 'livewire-flash::livewire.flash-overlay', 8 | ], 9 | 'styles' => [ 10 | 'info' => [ 11 | 'bg-color' => 'bg-blue-100', 12 | 'border-color' => 'border-blue-400', 13 | 'icon-color' => 'text-blue-400', 14 | 'text-color' => 'text-blue-800', 15 | 'icon' => 'fas fa-info-circle', 16 | ], 17 | 'success' => [ 18 | 'bg-color' => 'bg-green-100', 19 | 'border-color' => 'border-green-400', 20 | 'icon-color' => 'text-green-400', 21 | 'text-color' => 'text-green-800', 22 | 'icon' => 'fas fa-check', 23 | ], 24 | 'warning' => [ 25 | 'bg-color' => 'bg-yellow-100', 26 | 'border-color' => 'border-yellow-400', 27 | 'icon-color' => 'text-yellow-400', 28 | 'text-color' => 'text-yellow-800', 29 | 'icon' => 'fas fa-exclamation-circle', 30 | ], 31 | 'error' => [ 32 | 'bg-color' => 'bg-red-100', 33 | 'border-color' => 'border-red-400', 34 | 'icon-color' => 'text-red-400', 35 | 'text-color' => 'text-red-800', 36 | 'icon' => 'fas fa-exclamation-triangle', 37 | ], 38 | 'overlay' => [ 39 | 'overly-bg-color' => 'bg-gray-500', 40 | 'overlay-bg-opacity' => 'opacity-75', 41 | 42 | 'title-text-color' => 'text-gray-900', 43 | 44 | 'body-text-color' => 'text-gray-500', 45 | 46 | 'button-border-color' => 'border-transparent', 47 | 'button-bg-color' => 'bg-indigo-600', 48 | 'button-text-color' => 'text-white', 49 | 50 | 'button-hover-bg-color' => 'hover:bg-indigo-700', 51 | 'button-hover-text-color' => 'hover:text-white', 52 | 'button-focus-ring-color' => 'focus:ring-indigo-500', 53 | 54 | 'button-extra-classes' => '', 55 | 56 | 'button-text' => 'Close', 57 | ], 58 | ], 59 | ]; 60 | -------------------------------------------------------------------------------- /src/views/livewire/flash-container.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @foreach ($messages as $index => $message) 3 | @if ($message['overlay']) 4 | 5 | @else 6 | 7 | @endif 8 | @endforeach 9 |
10 | -------------------------------------------------------------------------------- /src/views/livewire/flash-message.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @if($shown) 3 |
4 |
5 | @if ($styles['icon'] ?? false) 6 |
7 |

8 | 9 |

10 |
11 | @endif 12 |
13 | {!! $message['message'] !!} 14 |
15 | @if ($message['dismissable'] ?? false) 16 |
17 |
18 | 21 |
22 |
23 | @endif 24 |
25 |
26 | @endif 27 |
28 | -------------------------------------------------------------------------------- /src/views/livewire/flash-overlay.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @if($shown) 3 |
4 |
5 | 15 | 18 | 19 | 20 | 21 | 31 | 59 |
60 |
61 | @endif 62 |
63 | --------------------------------------------------------------------------------