├── .DS_Store ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── bandwagon.php ├── database ├── factories │ └── ModelFactory.php └── migrations │ └── create_bandwagon_events_table.php.stub ├── docs ├── .DS_Store ├── .npmignore ├── deploy.sh ├── package-lock.json ├── package.json └── src │ ├── .DS_Store │ ├── .vuepress │ ├── components │ │ ├── Foo │ │ │ └── Bar.vue │ │ ├── OtherComponent.vue │ │ └── demo-component.vue │ ├── config.js │ ├── enhanceApp.js │ ├── public │ │ ├── Bandwagon.png │ │ ├── Bandwagon@2x.png │ │ ├── Bandwagon@3x.png │ │ ├── Bandwagon@4x.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── bandwagon-thumb.gif │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── preview.png │ │ ├── site.webmanifest │ │ └── social-share.png │ └── styles │ │ ├── index.styl │ │ └── palette.styl │ ├── config │ └── README.md │ ├── guide │ ├── README.md │ ├── cleaning-up.md │ ├── configuration.md │ ├── firing-events.md │ ├── getting-started.md │ └── rendering.md │ └── index.md ├── package-lock.json ├── package.json ├── public ├── app.js ├── app.js.LICENSE.txt └── mix-manifest.json ├── resources ├── js │ ├── BandwagonRenderer.vue │ └── app.js └── views │ ├── .gitkeep │ └── renderer.blade.php ├── src ├── Bandwagon.php ├── BandwagonFacade.php ├── BandwagonServiceProvider.php ├── Commands │ └── BandwagonCleanup.php ├── Events │ └── BandwagonEventCreated.php ├── Http │ ├── Controllers │ │ └── EventsController.php │ └── routes.php ├── Listeners │ └── RecordBandwagonEvent.php ├── Models │ └── BandwagonEvent.php ├── Providers │ └── EventServiceProvider.php └── View │ └── Components │ └── Renderer.php └── webpack.mix.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/.DS_Store -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `bandwagon` will be documented in this file 4 | 5 | ## 1.0.0 - 202X-XX-XX 6 | 7 | - initial release 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Bndwgn bvba 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ![preview](./docs/src/.vuepress/public/preview.png) 4 | 5 | This is a Laravel package to help promote social proof and legitimacy within your application. With a simple blade component added to any page you can share with potential customers or users that other customers are using and/or paying for your product. A simple pop-up will display in the corner of page with information such as "Someone from New York purchased the business plan 2 minutes ago." 6 | 7 | Full documentation can be found at [laravelbandwagon.com](https://www.laravelbandwagon.com) 8 | 9 | # Getting Started 10 | 11 | ## Installation 12 | 13 | You can install this package via composer using: 14 | 15 | ```sh 16 | composer require bndwgn/laravel-bandwagon 17 | ``` 18 | 19 | The package will automatically register its service provider. 20 | To publish the config file to `config/bandwagon.php` run: 21 | 22 | ```sh 23 | php artisan vendor:publish --provider="Bndwgn\Bandwagon\BandwagonServiceProvider" 24 | ``` 25 | 26 | ## Rendering the component 27 | 28 | To render the component just add the component to any or all desired pages like so: 29 | 30 | ![thumb](./docs/src/.vuepress/public/bandwagon-thumb.gif) 31 | 32 | ```html 33 | 34 | ``` 35 | ## Publishing an event to users 36 | 37 | To use the example of sharing a purchase with people who are on the purchase page of your application you would just add the following: 38 | ```php 39 | // App/Http/Controllers/PurchaseController.php 40 | use Bndwgn\Bandwagon\Bandwagon; 41 | 42 | public function purchase(Request $request, Product $product) 43 | { 44 | $user = Auth::user(); 45 | // ... logic to charge a customer 46 | Bandwagon::createEvent( 47 | "Someone from ${$user->state}", 48 | "Purchased the ${$product->displayName} plan", 49 | $request->ip(), 50 | route('purchase', $product->id) 51 | ); 52 | } 53 | ``` 54 | This will create a new Bandwagon record which then any users who are on the purchase page where you render the component (``) will see. 55 | 56 | ## Credits 57 | 58 | - [Alex Harris](https://github.com/chasenyc) 59 | - [All Contributors](../../contributors) 60 | 61 | ## License 62 | 63 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 64 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bndwgn/laravel-bandwagon", 3 | "description": "Social proof package for Laravel", 4 | "keywords": [ 5 | "bandwagon" 6 | ], 7 | "homepage": "https://github.com/bndwgn/laravel-bandwagon", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Alex Harris", 12 | "email": "alex@alexharris.nyc", 13 | "homepage": "https://github.com/chasenyc", 14 | "role": "Developer" 15 | } 16 | ], 17 | "require": { 18 | "php": "^7.4|^8.0", 19 | "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0" 20 | }, 21 | "require-dev": { 22 | "orchestra/testbench": "^6.0", 23 | "phpunit/phpunit": "^9.3", 24 | "vimeo/psalm": "^3.11" 25 | }, 26 | "autoload": { 27 | "exclude-from-classmap": ["/docs/", "/tests/"], 28 | "psr-4": { 29 | "Bndwgn\\Bandwagon\\": "src", 30 | "Bndwgn\\Bandwagon\\Database\\Factories\\": "database/factories" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Bndwgn\\Bandwagon\\Tests\\": "tests" 36 | } 37 | }, 38 | "scripts": { 39 | "psalm": "vendor/bin/psalm", 40 | "test": "vendor/bin/phpunit --colors=always", 41 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 42 | }, 43 | "config": { 44 | "sort-packages": true 45 | }, 46 | "extra": { 47 | "laravel": { 48 | "providers": [ 49 | "Bndwgn\\Bandwagon\\BandwagonServiceProvider" 50 | ], 51 | "aliases": { 52 | "Bandwagon": "Bndwgn\\Bandwagon\\BandwagonFacade" 53 | } 54 | } 55 | }, 56 | "minimum-stability": "dev", 57 | "prefer-stable": true 58 | } 59 | -------------------------------------------------------------------------------- /config/bandwagon.php: -------------------------------------------------------------------------------- 1 | env('BANDWAGON_POLL', 30), 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Display timing 20 | |-------------------------------------------------------------------------- 21 | | 22 | | This is how long the message will appear on the user's screen. This will 23 | | be passed to the blade component which dictates how long it appears for. 24 | */ 25 | 'display' => env('BANDWAGON_DISPLAY', 5), 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Delay 30 | |-------------------------------------------------------------------------- 31 | | 32 | | This is how long to wait once the page has been loaded before displaying 33 | | the first event (if one is available). If you want no delay just set 34 | | this value to 0. 35 | */ 36 | 'delay' => env('BANDWAGON_DELAY', 3), 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Oldest message to display 41 | |-------------------------------------------------------------------------- 42 | | 43 | | This is how long the message will appear on the user's screen. This will 44 | | be passed to the blade component which dictates how long it appears for. 45 | */ 46 | 'oldest' => env('BANDWAGON_OLDEST', 86400), 47 | 48 | /* 49 | |-------------------------------------------------------------------------- 50 | | Master switch 51 | |-------------------------------------------------------------------------- 52 | | 53 | | This will determine whether Bandwagon will display anything to users. 54 | */ 55 | 'enabled' => env('BANDWAGON_ENABLED', true), 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Cleanup 60 | |-------------------------------------------------------------------------- 61 | | 62 | | These keys are for use with cleaning up old Bandwagon Events. Events are 63 | | stored in the database and will continue to grow with time, depending on 64 | | your needs you may want to clear ones that will not be part of a query. 65 | | Any events that are older than the `oldest` config setting will never be 66 | | used or displayed to a user. It is recommended that you keep your 67 | | `olderthan` config the same or larger than the `oldest` key. 68 | */ 69 | 'cleanup' => [ 70 | 'enabled' => env('BANDWAGON_CLEANUP_ENABLED', true), 71 | 'olderthan' => env('BANDWAGON_CLEANUP_OLDER_THAN', 86400), 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Routes 77 | |-------------------------------------------------------------------------- 78 | | 79 | | These two values are for the api endpoint that exposes bandwagon 80 | | events to the client. 81 | */ 82 | 'domain' => env('BANDWAGON_DOMAIN', null), 83 | 'path' => env('BANDWAGON_PATH', 'bandwagon'), 84 | ]; 85 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('title'); 19 | $table->string('subtitle'); 20 | $table->string('ip', 50); 21 | $table->string('url'); 22 | $table->integer('event_at')->unsigned(); 23 | $table->timestamps(); 24 | 25 | $table->index('ip'); 26 | $table->index('event_at'); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('bandwagon_events'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/.DS_Store -------------------------------------------------------------------------------- /docs/.npmignore: -------------------------------------------------------------------------------- 1 | pids 2 | logs 3 | node_modules 4 | npm-debug.log 5 | coverage/ 6 | run 7 | dist 8 | .DS_Store 9 | .nyc_output 10 | .basement 11 | config.local.js 12 | basement_dist 13 | -------------------------------------------------------------------------------- /docs/deploy.sh: -------------------------------------------------------------------------------- 1 | 2 | # abort on errors 3 | set -e 4 | 5 | # build 6 | npm run build 7 | 8 | # navigate into the build output directory 9 | cd src/.vuepress/dist 10 | 11 | # if you are deploying to a custom domain 12 | echo 'www.laravelbandwagon.com' > CNAME 13 | 14 | git init 15 | git add -A 16 | git commit -m 'deploy' 17 | 18 | # if you are deploying to https://.github.io 19 | # git push -f git@github.com:/.github.io.git master 20 | 21 | # if you are deploying to https://.github.io/ 22 | git push -f git@github.com:bndwgn/laravel-bandwagon.git master:gh-pages 23 | 24 | cd - -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-bandwagon", 3 | "version": "0.0.1", 4 | "description": "Social proof package for Laravel", 5 | "main": "index.js", 6 | "authors": { 7 | "name": "Alex Harris", 8 | "email": "chasenyc@gmail.com" 9 | }, 10 | "repository": "github.com/bndwgn/laravel-bandwagon/laravel-bandwagon", 11 | "scripts": { 12 | "dev": "vuepress dev src", 13 | "build": "vuepress build src" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "vuepress": "^1.5.3", 18 | "vuepress-plugin-mailchimp": "^1.4.2" 19 | }, 20 | "dependencies": { 21 | "vuepress-plugin-code-copy": "^1.0.6" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.DS_Store -------------------------------------------------------------------------------- /docs/src/.vuepress/components/Foo/Bar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /docs/src/.vuepress/components/OtherComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /docs/src/.vuepress/components/demo-component.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /docs/src/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const { description } = require('../../package') 2 | 3 | module.exports = { 4 | /** 5 | * Ref:https://v1.vuepress.vuejs.org/config/#title 6 | */ 7 | title: 'Laravel Bandwagon', 8 | /** 9 | * Ref:https://v1.vuepress.vuejs.org/config/#description 10 | */ 11 | description: description, 12 | /** 13 | * Ref: https://vuepress.vuejs.org/guide/deploy.html#github-pages 14 | */ 15 | base: '/', 16 | 17 | /** 18 | * Extra tags to be injected to the page HTML `` 19 | * 20 | * ref:https://v1.vuepress.vuejs.org/config/#head 21 | */ 22 | head: [ 23 | ['meta', { name: 'theme-color', content: '#4E8FA4' }], 24 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 25 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }] 26 | ], 27 | 28 | /** 29 | * Theme configuration, here is the default theme configuration for VuePress. 30 | * 31 | * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html 32 | */ 33 | themeConfig: { 34 | repo: '', 35 | editLinks: false, 36 | docsDir: '', 37 | editLinkText: '', 38 | lastUpdated: false, 39 | nav: [ 40 | { 41 | text: 'Guide', 42 | link: '/guide/', 43 | }, 44 | { 45 | text: 'Author', 46 | link: 'https://github.com/chasenyc' 47 | }, 48 | { 49 | text: 'Source', 50 | link: 'https://github.com/bndwgn/laravel-bandwagon' 51 | } 52 | ], 53 | sidebar: { 54 | '/guide/': [ 55 | { 56 | title: 'Guide', 57 | collapsable: false, 58 | children: [ 59 | '', 60 | 'getting-started', 61 | 'configuration', 62 | 'cleaning-up', 63 | 'firing-events', 64 | 'rendering' 65 | ] 66 | } 67 | ], 68 | } 69 | }, 70 | 71 | /** 72 | * Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/ 73 | */ 74 | plugins: [ 75 | '@vuepress/plugin-back-to-top', 76 | '@vuepress/plugin-medium-zoom', 77 | [ 78 | "vuepress-plugin-mailchimp", 79 | { 80 | title: "Updates", 81 | content: "Stay up to date on any changes, no spam, ever.", 82 | // You need to provide this plugin with your Mailchimp endpoint in order for it 83 | // to know where to save the email address. See more detail in Config section. 84 | endpoint: "https://laravelbandwagon.us7.list-manage.com/subscribe/post?u=0145e96878d00632811cbb77e&id=22e4fcfd0f" 85 | } 86 | ] 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /docs/src/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Client app enhancement file. 3 | * 4 | * https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements 5 | */ 6 | 7 | export default ({ 8 | Vue, // the version of Vue being used in the VuePress app 9 | options, // the options for the root Vue instance 10 | router, // the router instance for the app 11 | siteData // site metadata 12 | }) => { 13 | // ...apply enhancements for the site. 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/.vuepress/public/Bandwagon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/Bandwagon.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/Bandwagon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/Bandwagon@2x.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/Bandwagon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/Bandwagon@3x.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/Bandwagon@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/Bandwagon@4x.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/bandwagon-thumb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/bandwagon-thumb.gif -------------------------------------------------------------------------------- /docs/src/.vuepress/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/favicon-16x16.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/favicon-32x32.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /docs/src/.vuepress/public/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/preview.png -------------------------------------------------------------------------------- /docs/src/.vuepress/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /docs/src/.vuepress/public/social-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/docs/src/.vuepress/public/social-share.png -------------------------------------------------------------------------------- /docs/src/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Styles here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/config/#index-styl 5 | */ 6 | 7 | .home .hero img 8 | max-width 450px!important 9 | 10 | pre .code-copy { 11 | margin-top: -0.85rem; 12 | } -------------------------------------------------------------------------------- /docs/src/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom palette here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl 5 | */ 6 | 7 | $accentColor = #4e8fa4 8 | $textColor = #2c3e50 9 | $borderColor = #eaecef 10 | $codeBgColor = #282c34 11 | -------------------------------------------------------------------------------- /docs/src/config/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: auto 3 | --- 4 | 5 | # Config 6 | 7 | ## foo 8 | 9 | - Type: `string` 10 | - Default: `/` 11 | 12 | ## bar 13 | 14 | - Type: `string` 15 | - Default: `/` 16 | -------------------------------------------------------------------------------- /docs/src/guide/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | foo 4 | 5 | This is a Laravel package to help promote social proof and legitimacy within your application. With a simple blade component added to any page you can share with potential customers or users that other customers are using and/or paying for your product. A simple pop-up will display in the corner of page with information such as "Someone from New York purchased the business plan 2 minutes ago." -------------------------------------------------------------------------------- /docs/src/guide/cleaning-up.md: -------------------------------------------------------------------------------- 1 | # Cleaning up old events 2 | 3 | [[toc]] 4 | 5 | ## Introduction 6 | 7 | Every time an event is created using the the `Bandwagon::createEvent()` command a record is stored in the database in a table called `bandwagon_events`. Depending on how many events are being fired you will most likely want to cleanup this table from time to time. Additionally the `bandwagon.php` config key `oldest` dictates the oldest event to display to users, thus rendering any events older than `oldest` useless for this packages purposes. 8 | 9 | ## Console command 10 | 11 | Out of the box you are provided with the following console command: 12 | ```sh 13 | php artisan bandwagon:cleanup 14 | ``` 15 | 16 | ## Scheduling 17 | It is recommended to add the command to run on some interval to ensure your table does not get too large by adding it `Kernel.php`: 18 | ```php 19 | // App/Console/Kernel.php 20 | 21 | protected function schedule(Schedule $schedule) 22 | { 23 | $schedule->command('bandwagon:cleanup')->daily(); 24 | } 25 | ``` 26 | 27 | ## Configuration 28 | This command will remove all events older than what is specified in the `bandwagon.php` config. You can see the specific key here: 29 | ```php 30 | // config/bandwagon.php 31 | 32 | 'cleanup' => [ 33 | 'enabled' => env('BANDWAGON_CLEANUP_ENABLED', true), 34 | 'olderthan' => env('BANDWAGON_CLEANUP_OLDER_THAN', 86400), 35 | ], 36 | ``` 37 | This will find any events older than `time() - config('bandwagon.cleanup.olderthan')` 38 | and delete them from the database to keep a maintainable size to this table. Feel free to adjust it as you would like but keep in mind it is best practice to make this value equal to or higher than the `oldest` key found in `bandwagon.php`. -------------------------------------------------------------------------------- /docs/src/guide/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | [[toc]] 4 | 5 | ## Key values 6 | 7 | There are a few simple configurations you can change through `config/bandwagon.php` that will all impact what and how messages are displayed to the end user: 8 | 9 | ```php 10 | 'poll' => env('BANDWAGON_POLL', 30)` 11 | ``` 12 | 13 | `poll` refers to how often (in seconds) the package will poll for new messages. 14 | 15 | ```php 16 | 'display' => env('BANDWAGON_DISPLAY', 5) 17 | ``` 18 | `display` refers to how long (in seconds) the message will stay on the user's screen. 19 | 20 | ```php 21 | 'delay' => env('BANDWAGON_DELAY', 3) 22 | ``` 23 | `delay` refers to how long (in seconds) before the first message displays, if there is one already available. 24 | 25 | ```php 26 | 'oldest' => env('BANDWAGON_OLDEST', 86400) 27 | ``` 28 | `oldest` refers to how old of an event to display to the user. This value is in seconds and defaults to 1 day. What that means is when a user goes to a page where we are displaying bandwagon events, when they first come to the page there will be a poll to get the most recent event that has occured in under one day, if one is found it will be displayed. 29 | 30 | ```php 31 | 'enabled' => env('BANDWAGON_ENABLED', true) 32 | ``` 33 | `enabled` is a master switch for whether or not bandwagon does anything. If this is disabled no Bandwagon events will be recorded and no messages will be displayed to the user. 34 | 35 | ## Cleanup 36 | 37 | For cleaning up old events there are a few keys that are used: 38 | ```php 39 | 'cleanup' => [ 40 | 'enabled' => env('BANDWAGON_CLEANUP_ENABLED', true), 41 | 'olderthan' => env('BANDWAGON_CLEANUP_OLDER_THAN', 86400), 42 | ], 43 | ``` 44 | These keys are for use with cleaning up old Bandwagon Events. Events are stored in the database and will continue to grow with time, depending on your needs you may want to clear ones that will not be part of a query. Any events that are older than the `oldest` config setting will never be used or displayed to a user. It is recommended that you keep your 45 | `olderthan` config the same or larger than the `oldest` key. 46 | 47 | ## Routes 48 | 49 | These two values are for the api endpoint that exposes bandwagon events to the client. 50 | ```php 51 | 'domain' => env('BANDWAGON_DOMAIN', null), 52 | 'path' => env('BANDWAGON_PATH', 'bandwagon'), 53 | ``` 54 | `path` refers to the path prefix for the endpoint. `domain` refers to domain value passed to `Route::group`. -------------------------------------------------------------------------------- /docs/src/guide/firing-events.md: -------------------------------------------------------------------------------- 1 | # Firing events 2 | 3 | [[toc]] 4 | 5 | ## Publishing a new event 6 | 7 | To use the example of sharing a purchase with people who are on the purchase page of your application you would just add the following: 8 | ```php 9 | Bandwagon::createEvent( 10 | "Someone from ${$user->state}", 11 | "purchased the ${$product->displayName} plan", 12 | $request->ip(), 13 | route('product', $product->id) 14 | ); 15 | ``` 16 | There are four parameters that are taken in by the `Bandwagon::createEvent(String $title, String $subtitle, String $ip, String $url)` function: 17 | 18 | | Property | Required | Description | 19 | | -----------| ------ | ----------- | 20 | | title | true | This is the text that appears on the first line of the message displayed to users. By default this text is slightly more prominent due to css making the font-weight slightly higher and the color slightly darker | 21 | | subtitle | true | This is the second line of text in the message displayed to users, it is more subtle than the first line by default | 22 | | ip | false | This is the ip address of the user who's action caused this event. 23 | | url | false | This will turn the notification into a clickable link that will route your users to a different page. 24 | 25 | ### Title 26 | The title will be the first line of text in the notification that users will see. By default the first line is darker and a heavier font-weight than the subtitle. This can be customized through altering the css classes passed to the component. A good example of a title line might be "Someone in New York, NY". This is the social proof aspect that is being emphasized, another human being purchased, viewing or used your product. 27 | 28 | ### Subtitle 29 | The subtitle is slightly less prominent and can also be altered through css, this field is required, if for any reason you would like to not include a second line you can always just pass an empty string. This line is suggested to be the action used by the consumer such as "purchased the annual plan" but can obviously be used in whatever way is desired. 30 | 31 | ### IP Address 32 | This field is not required, the purpose of this field is to allow for messages initiated by a user to be filtered out from display to that same user. For example, if there are three users, A, B and C, all three users are on the purchase page of a website, if user B were to make a purchase we would want to display that "Someone in New York, NY made a purchase 1 second ago" to user's A and C but we would not want to display this to user B, who made the purchase. If you would like for user B to see this message as well, you can omit the last parameter, the ip address, from `Bandwagon::createEvent()`. 33 | 34 | ## URL 35 | This field is not required. This will turn the toast notification into a clickable object to send your users to a different page. A good example of this would be if you were displaying that someone purchased a specific prodct, if the user were to click the notification they would be brought to that specific product page. 36 | 37 | ## Laravel Event 38 | The `Bandwagon::createEvent()` is a very simple wrapper that behind the scenes just fires a new Laravel Event. 39 | 40 | ### `Bndwgn\Bandwagon\Events\BandwagonEventCreated` 41 | The `BandwagonEventCreated` event has three public properties on it: 42 | ```php 43 | /** 44 | * The title for the message displayed to users 45 | */ 46 | public $title 47 | 48 | /** 49 | * The subtitle for the message displayed to users 50 | */ 51 | public $subtitle 52 | 53 | /** 54 | * The ip address of the user who generated the event, 55 | * this is nullable and should only be used if you want 56 | * to filter this event from being seen by the initiator 57 | * of this event. 58 | */ 59 | public $ip 60 | 61 | /** 62 | * This is a url that will make the notification clickable 63 | */ 64 | public $url; 65 | ``` 66 | 67 | ## Database 68 | 69 | ::: danger 70 | If you are upgrading from a version below 0.3.3 you will need to make a new migration to add the `url` column into the `bandwagon_events` table. 71 | ::: 72 | 73 | These events will then be stored in the database to a table named `bandwagon_events`. These events are stored by an event listener(`Bndwgn\Bandwagon\Listeners\RecordBandwagonEvent`). The databse schema is as follows: 74 | 75 | ```php 76 | Schema::create('bandwagon_events', function (Blueprint $table) { 77 | $table->bigIncrements('id'); 78 | $table->string('title'); 79 | $table->string('subtitle'); 80 | $table->string('ip', 50); 81 | $table->string('url'); 82 | $table->integer('event_at')->unsigned(); 83 | $table->timestamps(); 84 | 85 | $table->index('ip'); 86 | $table->index('event_at'); 87 | }); 88 | ``` 89 | 90 | This table has a corresponding model that is used under the hood `Bndwgn\Bandwagon\Models\BandwagonEvent`. 91 | 92 | -------------------------------------------------------------------------------- /docs/src/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | [[toc]] 4 | 5 | ## Installation 6 | 7 | You can install this package via composer using: 8 | 9 | ```sh 10 | composer require bndwgn/laravel-bandwagon 11 | ``` 12 | 13 | The package will automatically register its service provider. 14 | To publish the config file to `config/bandwagon.php` run: 15 | 16 | ```sh 17 | php artisan vendor:publish --provider="Bndwgn\Bandwagon\BandwagonServiceProvider" 18 | ``` 19 | 20 | ## Rendering the component 21 |
22 | component 23 | 24 | To render the component just add the component to any or all desired pages like so: 25 | ```html 26 | 27 | 28 | 29 | 30 | ``` 31 | Just make sure to put the component at the bottom of your body tag, outside of any other `
` tags. 32 | 33 | ## Publishing an event to users 34 | 35 | To use the example of sharing a purchase with people who are on the purchase page of your application you would just add the following: 36 | ```php 37 | // App/Http/Controllers/PurchaseController.php 38 | use Bndwgn\Bandwagon\Bandwagon; 39 | 40 | public function purchase(Request $request, Product $product) 41 | { 42 | $user = Auth::user(); 43 | // ... logic to charge a customer 44 | Bandwagon::createEvent( 45 | "Someone from ${$user->state}", 46 | "purchased the ${$product->displayName} plan", 47 | $request->ip(), // nullable 48 | route('product', $product->id) // nullable 49 | ); 50 | } 51 | ``` 52 | ::: tip 53 | Leave the ip address param off to make sure even the user who created the event sees the message. 54 | ::: 55 | 56 | This will create a new Bandwagon record which then any users who are on the purchase page where you render the component (``) will see. 57 | -------------------------------------------------------------------------------- /docs/src/guide/rendering.md: -------------------------------------------------------------------------------- 1 | # Frontend 2 | 3 | [[toc]] 4 | 5 | ## Rendering the component 6 | 7 | To render the component just add the component to any or all desired pages like so: 8 | ```html 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ``` 19 | ::: danger 20 | The `` component must be placed at the bottom of your html, outside of any div tags or it will not work properly 21 | ::: 22 | 23 | This component will be published into your codebase when you run the vendor publish command during installation: 24 | ```sh 25 | php artisan vendor:publish --provider="Bndwgn\Bandwagon\BandwagonServiceProvider" 26 | ``` 27 | You can find the blade in `resources\views\vendor\bandwagon\renderer.blade.php`. This blade view has some styling, the rendering of a Vue component and the necessary javascript to render said Vue component. 28 | 29 | ## Styling the component 30 | While bandwagon comes with some very basic default styling, every HTML tag rendered has a prop you can pass in to override the default classes. For example you could pass the following into the component to override the styling of the title field: 31 | 32 | ```php 33 | 34 | ``` 35 | which would override the title class with some tailwind styles. 36 | 37 | ### Styles override 38 | 39 | | Prop name | Description | 40 | | --------- | ----------- | 41 | | class-snackbar | This is the wrapping div for the whole component | 42 | | class-message | This is the message div that has the three lines of text within it | 43 | | class-title | This is the title line of the message | 44 | | class-subtitle | This is the subtitle line of the message | 45 | | class-time | This is the amount of time since the event occured | 46 | 47 | You can see a slightly dumbed down (for simplicity's sake) version of the full component being rendered to understand what you are styling here: 48 | 49 | ```vue 50 |
51 |
52 |

{{ title }}

53 |

{{ subtitle }}

54 |

{{ timeAgo() }}

55 |
56 |
57 | ``` -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | meta: 4 | - name: og:title 5 | content: Laravel Bandwagon 6 | - name: og:image 7 | content: /social-share.png 8 | - name: keywords 9 | content: social proof laravel package fomo socialproof 10 | heroImage: /Bandwagon.png 11 | tagline: Social proof package for Laravel 12 | actionText: Read the docs → 13 | actionLink: /guide/ 14 | features: 15 | - title: Built for Laravel 16 | details: Easy plug and play social proof solution for any Laravel project. Almost no code to be up and running. 17 | - title: Gain trust with customers 18 | details: Show your customers that other people are using and paying for your product. 19 | - title: Highly customizeable 20 | details: Take control of the way it renders, make it your own, through css and configuration. 21 | footer: Made by Alex Harris with ❤️ 22 | --- 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "npm run development", 4 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 5 | "watch": "npm run development -- --watch", 6 | "watch-poll": "npm run watch -- --watch-poll", 7 | "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", 8 | "prod": "npm run production", 9 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 10 | }, 11 | "devDependencies": { 12 | "axios": "^0.21.0", 13 | "laravel-mix": "^5.0.9", 14 | "vue": "^2.6.12", 15 | "vue-template-compiler": "^2.6.12" 16 | }, 17 | "dependencies": {} 18 | } 19 | -------------------------------------------------------------------------------- /public/app.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see app.js.LICENSE.txt */ 2 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},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 r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},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=13)}([function(e,t,n){"use strict";var r=n(3),i=Object.prototype.toString;function o(e){return"[object Array]"===i.call(e)}function a(e){return void 0===e}function s(e){return null!==e&&"object"==typeof e}function c(e){if("[object Object]"!==i.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function u(e){return"[object Function]"===i.call(e)}function l(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),o(e))for(var n=0,r=e.length;n=200&&e<300}};c.headers={common:{Accept:"application/json, text/plain, */*"}},r.forEach(["delete","get","head"],(function(e){c.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){c.headers[e]=r.merge(o)})),e.exports=c}).call(this,n(7))},function(e,t){var n,r,i=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function s(e){if(n===setTimeout)return setTimeout(e,0);if((n===o||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:o}catch(e){n=o}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var c,u=[],l=!1,f=-1;function p(){l&&c&&(l=!1,c.length?u=c.concat(u):f=-1,u.length&&d())}function d(){if(!l){var e=s(p);l=!0;for(var t=u.length;t;){for(c=u,u=[];++f1)for(var n=1;n=0)return;a[t]="set-cookie"===t?(a[t]?a[t]:[]).concat([n]):a[t]?a[t]+", "+n:n}})),a):a}},function(e,t,n){"use strict";var r=n(0);e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function i(e){var r=e;return t&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return e=i(window.location.href),function(t){var n=r.isString(t)?i(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return!0}},function(e,t,n){"use strict";var r=n(11);function i(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var n=this;e((function(e){n.reason||(n.reason=new r(e),t(n.reason))}))}i.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},i.source=function(){var e;return{token:new i((function(t){e=t})),cancel:e}},e.exports=i},function(e,t,n){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,n){"use strict";(function(t,n){var r=Object.freeze({});function i(e){return null==e}function o(e){return null!=e}function a(e){return!0===e}function s(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function c(e){return null!==e&&"object"==typeof e}var u=Object.prototype.toString;function l(e){return"[object Object]"===u.call(e)}function f(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function p(e){return o(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function d(e){return null==e?"":Array.isArray(e)||l(e)&&e.toString===u?JSON.stringify(e,null,2):String(e)}function v(e){var t=parseFloat(e);return isNaN(t)?e:t}function h(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}var _=Object.prototype.hasOwnProperty;function b(e,t){return _.call(e,t)}function w(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var x=/-(\w)/g,$=w((function(e){return e.replace(x,(function(e,t){return t?t.toUpperCase():""}))})),C=w((function(e){return e.charAt(0).toUpperCase()+e.slice(1)})),A=/\B([A-Z])/g,k=w((function(e){return e.replace(A,"-$1").toLowerCase()})),S=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function O(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function T(e,t){for(var n in t)e[n]=t[n];return e}function E(e){for(var t={},n=0;n0,Y=X&&X.indexOf("edge/")>0,Q=(X&&X.indexOf("android"),X&&/iphone|ipad|ipod|ios/.test(X)||"ios"===W),ee=(X&&/chrome\/\d+/.test(X),X&&/phantomjs/.test(X),X&&X.match(/firefox\/(\d+)/)),te={}.watch,ne=!1;if(K)try{var re={};Object.defineProperty(re,"passive",{get:function(){ne=!0}}),window.addEventListener("test-passive",null,re)}catch(r){}var ie=function(){return void 0===z&&(z=!K&&!J&&void 0!==t&&t.process&&"server"===t.process.env.VUE_ENV),z},oe=K&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function ae(e){return"function"==typeof e&&/native code/.test(e.toString())}var se,ce="undefined"!=typeof Symbol&&ae(Symbol)&&"undefined"!=typeof Reflect&&ae(Reflect.ownKeys);se="undefined"!=typeof Set&&ae(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var ue=j,le=0,fe=function(){this.id=le++,this.subs=[]};fe.prototype.addSub=function(e){this.subs.push(e)},fe.prototype.removeSub=function(e){y(this.subs,e)},fe.prototype.depend=function(){fe.target&&fe.target.addDep(this)},fe.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t-1)if(o&&!b(i,"default"))a=!1;else if(""===a||a===k(e)){var c=Ue(String,i.type);(c<0||s0&&(lt((c=e(c,(n||"")+"_"+r))[0])&<(l)&&(f[u]=ye(l.text+c[0].text),c.shift()),f.push.apply(f,c)):s(c)?lt(l)?f[u]=ye(l.text+c):""!==c&&f.push(ye(c)):lt(c)&<(l)?f[u]=ye(l.text+c.text):(a(t._isVList)&&o(c.tag)&&i(c.key)&&o(n)&&(c.key="__vlist"+n+"_"+r+"__"),f.push(c)));return f}(e):void 0}function lt(e){return o(e)&&o(e.text)&&!1===e.isComment}function ft(e,t){if(e){for(var n=Object.create(null),r=ce?Reflect.ownKeys(e):Object.keys(e),i=0;i0,a=e?!!e.$stable:!o,s=e&&e.$key;if(e){if(e._normalized)return e._normalized;if(a&&n&&n!==r&&s===n.$key&&!o&&!n.$hasNormal)return n;for(var c in i={},e)e[c]&&"$"!==c[0]&&(i[c]=ht(t,c,e[c]))}else i={};for(var u in t)u in i||(i[u]=mt(t,u));return e&&Object.isExtensible(e)&&(e._normalized=i),H(i,"$stable",a),H(i,"$key",s),H(i,"$hasNormal",o),i}function ht(e,t,n){var r=function(){var e=arguments.length?n.apply(null,arguments):n({});return(e=e&&"object"==typeof e&&!Array.isArray(e)?[e]:ut(e))&&(0===e.length||1===e.length&&e[0].isComment)?void 0:e};return n.proxy&&Object.defineProperty(e,t,{get:r,enumerable:!0,configurable:!0}),r}function mt(e,t){return function(){return e[t]}}function gt(e,t){var n,r,i,a,s;if(Array.isArray(e)||"string"==typeof e)for(n=new Array(e.length),r=0,i=e.length;rdocument.createEvent("Event").timeStamp&&(cn=function(){return un.now()})}function ln(){var e,t;for(sn=cn(),on=!0,en.sort((function(e,t){return e.id-t.id})),an=0;anan&&en[n].id>e.id;)n--;en.splice(n+1,0,e)}else en.push(e);rn||(rn=!0,tt(ln))}}(this)},pn.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||c(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){He(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},pn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},pn.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},pn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||y(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var dn={enumerable:!0,configurable:!0,get:j,set:j};function vn(e,t,n){dn.get=function(){return this[t][n]},dn.set=function(e){this[t][n]=e},Object.defineProperty(e,n,dn)}var hn={lazy:!0};function mn(e,t,n){var r=!ie();"function"==typeof n?(dn.get=r?gn(t):yn(n),dn.set=j):(dn.get=n.get?r&&!1!==n.cache?gn(t):yn(n.get):j,dn.set=n.set||j),Object.defineProperty(e,t,dn)}function gn(e){return function(){var t=this._computedWatchers&&this._computedWatchers[e];if(t)return t.dirty&&t.evaluate(),fe.target&&t.depend(),t.value}}function yn(e){return function(){return e.call(this,this)}}function _n(e,t,n,r){return l(n)&&(r=n,n=n.handler),"string"==typeof n&&(n=e[n]),e.$watch(t,n,r)}var bn=0;function wn(e){var t=e.options;if(e.super){var n=wn(e.super);if(n!==e.superOptions){e.superOptions=n;var r=function(e){var t,n=e.options,r=e.sealedOptions;for(var i in n)n[i]!==r[i]&&(t||(t={}),t[i]=n[i]);return t}(e);r&&T(e.extendOptions,r),(t=e.options=De(n,e.extendOptions)).name&&(t.components[t.name]=e)}}return t}function xn(e){this._init(e)}function $n(e){return e&&(e.Ctor.options.name||e.tag)}function Cn(e,t){return Array.isArray(e)?e.indexOf(t)>-1:"string"==typeof e?e.split(",").indexOf(t)>-1:(n=e,"[object RegExp]"===u.call(n)&&e.test(t));var n}function An(e,t){var n=e.cache,r=e.keys,i=e._vnode;for(var o in n){var a=n[o];if(a){var s=$n(a.componentOptions);s&&!t(s)&&kn(n,o,r,i)}}}function kn(e,t,n,r){var i=e[t];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),e[t]=null,y(n,t)}!function(e){e.prototype._init=function(e){var t=this;t._uid=bn++,t._isVue=!0,e&&e._isComponent?function(e,t){var n=e.$options=Object.create(e.constructor.options),r=t._parentVnode;n.parent=t.parent,n._parentVnode=r;var i=r.componentOptions;n.propsData=i.propsData,n._parentListeners=i.listeners,n._renderChildren=i.children,n._componentTag=i.tag,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}(t,e):t.$options=De(wn(t.constructor),e||{},t),t._renderProxy=t,t._self=t,function(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}(t),function(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&Wt(e,t)}(t),function(e){e._vnode=null,e._staticTrees=null;var t=e.$options,n=e.$vnode=t._parentVnode,i=n&&n.context;e.$slots=pt(t._renderChildren,i),e.$scopedSlots=r,e._c=function(t,n,r,i){return Ft(e,t,n,r,i,!1)},e.$createElement=function(t,n,r,i){return Ft(e,t,n,r,i,!0)};var o=n&&n.data;Se(e,"$attrs",o&&o.attrs||r,null,!0),Se(e,"$listeners",t._parentListeners||r,null,!0)}(t),Qt(t,"beforeCreate"),function(e){var t=ft(e.$options.inject,e);t&&(Ce(!1),Object.keys(t).forEach((function(n){Se(e,n,t[n])})),Ce(!0))}(t),function(e){e._watchers=[];var t=e.$options;t.props&&function(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[];e.$parent&&Ce(!1);var o=function(o){i.push(o);var a=Re(o,t,n,e);Se(r,o,a),o in e||vn(e,"_props",o)};for(var a in t)o(a);Ce(!0)}(e,t.props),t.methods&&function(e,t){for(var n in e.$options.props,t)e[n]="function"!=typeof t[n]?j:S(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){de();try{return e.call(t,t)}catch(e){return He(e,t,"data()"),{}}finally{ve()}}(t,e):t||{})||(t={});for(var n,r=Object.keys(t),i=e.$options.props,o=(e.$options.methods,r.length);o--;){var a=r[o];i&&b(i,a)||(void 0,36!==(n=(a+"").charCodeAt(0))&&95!==n&&vn(e,"_data",a))}ke(t,!0)}(e):ke(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=ie();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new pn(e,a||j,j,hn)),i in e||mn(e,i,o)}}(e,t.computed),t.watch&&t.watch!==te&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i1?O(t):t;for(var n=O(arguments,1),r='event handler for "'+e+'"',i=0,o=t.length;iparseInt(this.max)&&kn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};!function(e){var t={get:function(){return B}};Object.defineProperty(e,"config",t),e.util={warn:ue,extend:T,mergeOptions:De,defineReactive:Se},e.set=Oe,e.delete=Te,e.nextTick=tt,e.observable=function(e){return ke(e),e},e.options=Object.create(null),R.forEach((function(t){e.options[t+"s"]=Object.create(null)})),e.options._base=e,T(e.options.components,On),function(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=O(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}(e),function(e){e.mixin=function(e){return this.options=De(this.options,e),this}}(e),function(e){e.cid=0;var t=1;e.extend=function(e){e=e||{};var n=this,r=n.cid,i=e._Ctor||(e._Ctor={});if(i[r])return i[r];var o=e.name||n.options.name,a=function(e){this._init(e)};return(a.prototype=Object.create(n.prototype)).constructor=a,a.cid=t++,a.options=De(n.options,e),a.super=n,a.options.props&&function(e){var t=e.options.props;for(var n in t)vn(e.prototype,"_props",n)}(a),a.options.computed&&function(e){var t=e.options.computed;for(var n in t)mn(e.prototype,n,t[n])}(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,R.forEach((function(e){a[e]=n[e]})),o&&(a.options.components[o]=a),a.superOptions=n.options,a.extendOptions=e,a.sealedOptions=T({},a.options),i[r]=a,a}}(e),function(e){R.forEach((function(t){e[t]=function(e,n){return n?("component"===t&&l(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}}))}(e)}(xn),Object.defineProperty(xn.prototype,"$isServer",{get:ie}),Object.defineProperty(xn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(xn,"FunctionalRenderContext",{value:Nt}),xn.version="2.6.12";var Tn=h("style,class"),En=h("input,textarea,option,select,progress"),jn=function(e,t,n){return"value"===n&&En(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},Nn=h("contenteditable,draggable,spellcheck"),Ln=h("events,caret,typing,plaintext-only"),Mn=h("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),In="http://www.w3.org/1999/xlink",Dn=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Pn=function(e){return Dn(e)?e.slice(6,e.length):""},Rn=function(e){return null==e||!1===e};function Fn(e,t){return{staticClass:Bn(e.staticClass,t.staticClass),class:o(e.class)?[e.class,t.class]:t.class}}function Bn(e,t){return e?t?e+" "+t:e:t||""}function Un(e){return Array.isArray(e)?function(e){for(var t,n="",r=0,i=e.length;r-1?pr(e,t,n):Mn(t)?Rn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):Nn(t)?e.setAttribute(t,function(e,t){return Rn(t)||"false"===t?"false":"contenteditable"===e&&Ln(t)?t:"true"}(t,n)):Dn(t)?Rn(n)?e.removeAttributeNS(In,Pn(t)):e.setAttributeNS(In,t,n):pr(e,t,n)}function pr(e,t,n){if(Rn(n))e.removeAttribute(t);else{if(G&&!Z&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var dr={create:lr,update:lr};function vr(e,t){var n=t.elm,r=t.data,a=e.data;if(!(i(r.staticClass)&&i(r.class)&&(i(a)||i(a.staticClass)&&i(a.class)))){var s=function(e){for(var t=e.data,n=e,r=e;o(r.componentInstance);)(r=r.componentInstance._vnode)&&r.data&&(t=Fn(r.data,t));for(;o(n=n.parent);)n&&n.data&&(t=Fn(t,n.data));return function(e,t){return o(e)||o(t)?Bn(e,Un(t)):""}(t.staticClass,t.class)}(t),c=n._transitionClasses;o(c)&&(s=Bn(s,Un(c))),s!==n._prevClass&&(n.setAttribute("class",s),n._prevClass=s)}}var hr,mr,gr,yr,_r,br,wr={create:vr,update:vr},xr=/[\w).+\-_$\]]/;function $r(e){var t,n,r,i,o,a=!1,s=!1,c=!1,u=!1,l=0,f=0,p=0,d=0;for(r=0;r=0&&" "===(h=e.charAt(v));v--);h&&xr.test(h)||(u=!0)}}else void 0===i?(d=r+1,i=e.slice(0,r).trim()):m();function m(){(o||(o=[])).push(e.slice(d,r).trim()),d=r+1}if(void 0===i?i=e.slice(0,r).trim():0!==d&&m(),o)for(r=0;r-1?{exp:e.slice(0,yr),key:'"'+e.slice(yr+1)+'"'}:{exp:e,key:null};for(mr=e,yr=_r=br=0;!Br();)Ur(gr=Fr())?zr(gr):91===gr&&Hr(gr);return{exp:e.slice(0,_r),key:e.slice(_r+1,br)}}(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function Fr(){return mr.charCodeAt(++yr)}function Br(){return yr>=hr}function Ur(e){return 34===e||39===e}function Hr(e){var t=1;for(_r=yr;!Br();)if(Ur(e=Fr()))zr(e);else if(91===e&&t++,93===e&&t--,0===t){br=yr;break}}function zr(e){for(var t=e;!Br()&&(e=Fr())!==t;);}var qr,Vr="__r";function Kr(e,t,n){var r=qr;return function i(){null!==t.apply(null,arguments)&&Xr(e,i,n,r)}}var Jr=Je&&!(ee&&Number(ee[1])<=53);function Wr(e,t,n,r){if(Jr){var i=sn,o=t;t=o._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=i||e.timeStamp<=0||e.target.ownerDocument!==document)return o.apply(this,arguments)}}qr.addEventListener(e,t,ne?{capture:n,passive:r}:n)}function Xr(e,t,n,r){(r||qr).removeEventListener(e,t._wrapper||t,n)}function Gr(e,t){if(!i(e.data.on)||!i(t.data.on)){var n=t.data.on||{},r=e.data.on||{};qr=t.elm,function(e){if(o(e.__r)){var t=G?"change":"input";e[t]=[].concat(e.__r,e[t]||[]),delete e.__r}o(e.__c)&&(e.change=[].concat(e.__c,e.change||[]),delete e.__c)}(n),at(n,r,Wr,Xr,Kr,t.context),qr=void 0}}var Zr,Yr={create:Gr,update:Gr};function Qr(e,t){if(!i(e.data.domProps)||!i(t.data.domProps)){var n,r,a=t.elm,s=e.data.domProps||{},c=t.data.domProps||{};for(n in o(c.__ob__)&&(c=t.data.domProps=T({},c)),s)n in c||(a[n]="");for(n in c){if(r=c[n],"textContent"===n||"innerHTML"===n){if(t.children&&(t.children.length=0),r===s[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===n&&"PROGRESS"!==a.tagName){a._value=r;var u=i(r)?"":String(r);ei(a,u)&&(a.value=u)}else if("innerHTML"===n&&qn(a.tagName)&&i(a.innerHTML)){(Zr=Zr||document.createElement("div")).innerHTML=""+r+"";for(var l=Zr.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;l.firstChild;)a.appendChild(l.firstChild)}else if(r!==s[n])try{a[n]=r}catch(e){}}}}function ei(e,t){return!e.composing&&("OPTION"===e.tagName||function(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}(e,t)||function(e,t){var n=e.value,r=e._vModifiers;if(o(r)){if(r.number)return v(n)!==v(t);if(r.trim)return n.trim()!==t.trim()}return n!==t}(e,t))}var ti={create:Qr,update:Qr},ni=w((function(e){var t={},n=/:(.+)/;return e.split(/;(?![^(]*\))/g).forEach((function(e){if(e){var r=e.split(n);r.length>1&&(t[r[0].trim()]=r[1].trim())}})),t}));function ri(e){var t=ii(e.style);return e.staticStyle?T(e.staticStyle,t):t}function ii(e){return Array.isArray(e)?E(e):"string"==typeof e?ni(e):e}var oi,ai=/^--/,si=/\s*!important$/,ci=function(e,t,n){if(ai.test(t))e.style.setProperty(t,n);else if(si.test(n))e.style.setProperty(k(t),n.replace(si,""),"important");else{var r=li(t);if(Array.isArray(n))for(var i=0,o=n.length;i-1?t.split(di).forEach((function(t){return e.classList.add(t)})):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function hi(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(di).forEach((function(t){return e.classList.remove(t)})):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function mi(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&T(t,gi(e.name||"v")),T(t,e),t}return"string"==typeof e?gi(e):void 0}}var gi=w((function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}})),yi=K&&!Z,_i="transition",bi="animation",wi="transition",xi="transitionend",$i="animation",Ci="animationend";yi&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(wi="WebkitTransition",xi="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&($i="WebkitAnimation",Ci="webkitAnimationEnd"));var Ai=K?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()};function ki(e){Ai((function(){Ai(e)}))}function Si(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),vi(e,t))}function Oi(e,t){e._transitionClasses&&y(e._transitionClasses,t),hi(e,t)}function Ti(e,t,n){var r=ji(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===_i?xi:Ci,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout((function(){c0&&(n=_i,l=a,f=o.length):t===bi?u>0&&(n=bi,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?_i:bi:null)?n===_i?o.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===_i&&Ei.test(r[wi+"Property"])}}function Ni(e,t){for(;e.length1}function Ri(e,t){!0!==t.data.show&&Mi(t)}var Fi=function(e){var t,n,r={},c=e.modules,u=e.nodeOps;for(t=0;tv?_(e,i(n[g+1])?null:n[g+1].elm,n,d,g,r):d>g&&w(t,p,v)}(p,h,g,n,l):o(g)?(o(e.text)&&u.setTextContent(p,""),_(p,null,g,0,g.length-1,n)):o(h)?w(h,0,h.length-1):o(e.text)&&u.setTextContent(p,""):e.text!==t.text&&u.setTextContent(p,t.text),o(v)&&o(d=v.hook)&&o(d=d.postpatch)&&d(e,t)}}}function A(e,t,n){if(a(n)&&o(e.parent))e.parent.data.pendingInsert=t;else for(var r=0;r-1,a.selected!==o&&(a.selected=o);else if(M(qi(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function zi(e,t){return t.every((function(t){return!M(t,e)}))}function qi(e){return"_value"in e?e._value:e.value}function Vi(e){e.target.composing=!0}function Ki(e){e.target.composing&&(e.target.composing=!1,Ji(e.target,"input"))}function Ji(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function Wi(e){return!e.componentInstance||e.data&&e.data.transition?e:Wi(e.componentInstance._vnode)}var Xi={model:Bi,show:{bind:function(e,t,n){var r=t.value,i=(n=Wi(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;r&&i?(n.data.show=!0,Mi(n,(function(){e.style.display=o}))):e.style.display=r?o:"none"},update:function(e,t,n){var r=t.value;!r!=!t.oldValue&&((n=Wi(n)).data&&n.data.transition?(n.data.show=!0,r?Mi(n,(function(){e.style.display=e.__vOriginalDisplay})):Ii(n,(function(){e.style.display="none"}))):e.style.display=r?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}}},Gi={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Zi(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?Zi(qt(t.children)):e}function Yi(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[$(o)]=i[o];return t}function Qi(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}var eo=function(e){return e.tag||zt(e)},to=function(e){return"show"===e.name},no={name:"transition",props:Gi,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&&(n=n.filter(eo)).length){var r=this.mode,i=n[0];if(function(e){for(;e=e.parent;)if(e.data.transition)return!0}(this.$vnode))return i;var o=Zi(i);if(!o)return i;if(this._leaving)return Qi(e,i);var a="__transition-"+this._uid+"-";o.key=null==o.key?o.isComment?a+"comment":a+o.tag:s(o.key)?0===String(o.key).indexOf(a)?o.key:a+o.key:o.key;var c=(o.data||(o.data={})).transition=Yi(this),u=this._vnode,l=Zi(u);if(o.data.directives&&o.data.directives.some(to)&&(o.data.show=!0),l&&l.data&&!function(e,t){return t.key===e.key&&t.tag===e.tag}(o,l)&&!zt(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=T({},c);if("out-in"===r)return this._leaving=!0,st(f,"afterLeave",(function(){t._leaving=!1,t.$forceUpdate()})),Qi(e,i);if("in-out"===r){if(zt(o))return u;var p,d=function(){p()};st(c,"afterEnter",d),st(c,"enterCancelled",d),st(f,"delayLeave",(function(e){p=e}))}}return i}}},ro=T({tag:String,moveClass:String},Gi);function io(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function oo(e){e.data.newPos=e.elm.getBoundingClientRect()}function ao(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}delete ro.mode;var so={Transition:no,TransitionGroup:{props:ro,beforeMount:function(){var e=this,t=this._update;this._update=function(n,r){var i=Gt(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,i(),t.call(e,n,r)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=Yi(this),s=0;s-1?Jn[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:Jn[e]=/HTMLUnknownElement/.test(t.toString())},T(xn.options.directives,Xi),T(xn.options.components,so),xn.prototype.__patch__=K?Fi:j,xn.prototype.$mount=function(e,t){return function(e,t,n){var r;return e.$el=t,e.$options.render||(e.$options.render=ge),Qt(e,"beforeMount"),r=function(){e._update(e._render(),n)},new pn(e,r,j,{before:function(){e._isMounted&&!e._isDestroyed&&Qt(e,"beforeUpdate")}},!0),n=!1,null==e.$vnode&&(e._isMounted=!0,Qt(e,"mounted")),e}(this,e=e&&K?Xn(e):void 0,t)},K&&setTimeout((function(){B.devtools&&oe&&oe.emit("init",xn)}),0);var co,uo=/\{\{((?:.|\r?\n)+?)\}\}/g,lo=/[-.*+?^${}()|[\]\/\\]/g,fo=w((function(e){var t=e[0].replace(lo,"\\$&"),n=e[1].replace(lo,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")})),po={staticKeys:["staticClass"],transformNode:function(e,t){t.warn;var n=Mr(e,"class");n&&(e.staticClass=JSON.stringify(n));var r=Lr(e,"class",!1);r&&(e.classBinding=r)},genData:function(e){var t="";return e.staticClass&&(t+="staticClass:"+e.staticClass+","),e.classBinding&&(t+="class:"+e.classBinding+","),t}},vo={staticKeys:["staticStyle"],transformNode:function(e,t){t.warn;var n=Mr(e,"style");n&&(e.staticStyle=JSON.stringify(ni(n)));var r=Lr(e,"style",!1);r&&(e.styleBinding=r)},genData:function(e){var t="";return e.staticStyle&&(t+="staticStyle:"+e.staticStyle+","),e.styleBinding&&(t+="style:("+e.styleBinding+"),"),t}},ho=h("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),mo=h("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),go=h("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),yo=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,_o=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,bo="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+U.source+"]*",wo="((?:"+bo+"\\:)?"+bo+")",xo=new RegExp("^<"+wo),$o=/^\s*(\/?)>/,Co=new RegExp("^<\\/"+wo+"[^>]*>"),Ao=/^]+>/i,ko=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},jo=/&(?:lt|gt|quot|amp|#39);/g,No=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Lo=h("pre,textarea",!0),Mo=function(e,t){return e&&Lo(e)&&"\n"===t[0]};function Io(e,t){var n=t?No:jo;return e.replace(n,(function(e){return Eo[e]}))}var Do,Po,Ro,Fo,Bo,Uo,Ho,zo,qo=/^@|^v-on:/,Vo=/^v-|^@|^:|^#/,Ko=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,Jo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Wo=/^\(|\)$/g,Xo=/^\[.*\]$/,Go=/:(.*)$/,Zo=/^:|^\.|^v-bind:/,Yo=/\.[^.\]]+(?=[^\]]*$)/g,Qo=/^v-slot(:|$)|^#/,ea=/[\r\n]/,ta=/\s+/g,na=w((function(e){return(co=co||document.createElement("div")).innerHTML=e,co.textContent})),ra="_empty_";function ia(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:la(t),rawAttrsMap:{},parent:n,children:[]}}function oa(e,t){var n,r;(r=Lr(n=e,"key"))&&(n.key=r),e.plain=!e.key&&!e.scopedSlots&&!e.attrsList.length,function(e){var t=Lr(e,"ref");t&&(e.ref=t,e.refInFor=function(e){for(var t=e;t;){if(void 0!==t.for)return!0;t=t.parent}return!1}(e))}(e),function(e){var t;"template"===e.tag?(t=Mr(e,"scope"),e.slotScope=t||Mr(e,"slot-scope")):(t=Mr(e,"slot-scope"))&&(e.slotScope=t);var n=Lr(e,"slot");if(n&&(e.slotTarget='""'===n?'"default"':n,e.slotTargetDynamic=!(!e.attrsMap[":slot"]&&!e.attrsMap["v-bind:slot"]),"template"===e.tag||e.slotScope||Or(e,"slot",n,function(e,t){return e.rawAttrsMap[":"+t]||e.rawAttrsMap["v-bind:"+t]||e.rawAttrsMap[t]}(e,"slot"))),"template"===e.tag){var r=Ir(e,Qo);if(r){var i=ca(r),o=i.name,a=i.dynamic;e.slotTarget=o,e.slotTargetDynamic=a,e.slotScope=r.value||ra}}else{var s=Ir(e,Qo);if(s){var c=e.scopedSlots||(e.scopedSlots={}),u=ca(s),l=u.name,f=u.dynamic,p=c[l]=ia("template",[],e);p.slotTarget=l,p.slotTargetDynamic=f,p.children=e.children.filter((function(e){if(!e.slotScope)return e.parent=p,!0})),p.slotScope=s.value||ra,e.children=[],e.plain=!1}}}(e),function(e){"slot"===e.tag&&(e.slotName=Lr(e,"name"))}(e),function(e){var t;(t=Lr(e,"is"))&&(e.component=t),null!=Mr(e,"inline-template")&&(e.inlineTemplate=!0)}(e);for(var i=0;i-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Nr(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Rr(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Rr(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Rr(t,"$$c")+"}",null,!0)}(e,r,i);else if("input"===o&&"radio"===a)!function(e,t,n){var r=n&&n.number,i=Lr(e,"value")||"null";Sr(e,"checked","_q("+t+","+(i=r?"_n("+i+")":i)+")"),Nr(e,"change",Rr(t,i),null,!0)}(e,r,i);else if("input"===o||"textarea"===o)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?Vr:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=Rr(t,l);c&&(f="if($event.target.composing)return;"+f),Sr(e,"value","("+t+")"),Nr(e,u,f,null,!0),(s||a)&&Nr(e,"blur","$forceUpdate()")}(e,r,i);else if(!B.isReservedTag(o))return Pr(e,r,i),!1;return!0},text:function(e,t){t.value&&Sr(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&Sr(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:ho,mustUseProp:jn,canBeLeftOpenTag:mo,isReservedTag:Vn,getTagNamespace:Kn,staticKeys:function(e){return e.reduce((function(e,t){return e.concat(t.staticKeys||[])}),[]).join(",")}(ma)},ya=w((function(e){return h("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(e?","+e:""))}));var _a=/^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/,ba=/\([^)]*?\);*$/,wa=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,xa={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},$a={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Ca=function(e){return"if("+e+")return null;"},Aa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Ca("$event.target !== $event.currentTarget"),ctrl:Ca("!$event.ctrlKey"),shift:Ca("!$event.shiftKey"),alt:Ca("!$event.altKey"),meta:Ca("!$event.metaKey"),left:Ca("'button' in $event && $event.button !== 0"),middle:Ca("'button' in $event && $event.button !== 1"),right:Ca("'button' in $event && $event.button !== 2")};function ka(e,t){var n=t?"nativeOn:":"on:",r="",i="";for(var o in e){var a=Sa(e[o]);e[o]&&e[o].dynamic?i+=o+","+a+",":r+='"'+o+'":'+a+","}return r="{"+r.slice(0,-1)+"}",i?n+"_d("+r+",["+i.slice(0,-1)+"])":n+r}function Sa(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map((function(e){return Sa(e)})).join(",")+"]";var t=wa.test(e.value),n=_a.test(e.value),r=wa.test(e.value.replace(ba,""));if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(Aa[s])o+=Aa[s],xa[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=Ca(["ctrl","shift","alt","meta"].filter((function(e){return!c[e]})).map((function(e){return"$event."+e+"Key"})).join("||"))}else a.push(s);return a.length&&(i+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map(Oa).join("&&")+")return null;"}(a)),o&&(i+=o),"function($event){"+i+(t?"return "+e.value+"($event)":n?"return ("+e.value+")($event)":r?"return "+e.value:e.value)+"}"}return t||n?e.value:"function($event){"+(r?"return "+e.value:e.value)+"}"}function Oa(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=xa[e],r=$a[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var Ta={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:j},Ea=function(e){this.options=e,this.warn=e.warn||Ar,this.transforms=kr(e.modules,"transformCode"),this.dataGenFns=kr(e.modules,"genData"),this.directives=T(T({},Ta),e.directives);var t=e.isReservedTag||N;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function ja(e,t){var n=new Ea(t);return{render:"with(this){return "+(e?Na(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function Na(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return La(e,t);if(e.once&&!e.onceProcessed)return Ma(e,t);if(e.for&&!e.forProcessed)return Da(e,t);if(e.if&&!e.ifProcessed)return Ia(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=Ba(e,t),i="_t("+n+(r?","+r:""),o=e.attrs||e.dynamicAttrs?za((e.attrs||[]).concat(e.dynamicAttrs||[]).map((function(e){return{name:$(e.name),value:e.value,dynamic:e.dynamic}}))):null,a=e.attrsMap["v-bind"];return!o&&!a||r||(i+=",null"),o&&(i+=","+o),a&&(i+=(o?"":",null")+","+a),i+")"}(e,t);var n;if(e.component)n=function(e,t,n){var r=t.inlineTemplate?null:Ba(t,n,!0);return"_c("+e+","+Pa(t,n)+(r?","+r:"")+")"}(e.component,e,t);else{var r;(!e.plain||e.pre&&t.maybeComponent(e))&&(r=Pa(e,t));var i=e.inlineTemplate?null:Ba(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var n=e.children[0];if(n&&1===n.type){var r=ja(n,t.options);return"inlineTemplate:{render:function(){"+r.render+"},staticRenderFns:["+r.staticRenderFns.map((function(e){return"function(){"+e+"}"})).join(",")+"]}"}}(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.dynamicAttrs&&(n="_b("+n+',"'+e.tag+'",'+za(e.dynamicAttrs)+")"),e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function Ra(e){return 1===e.type&&("slot"===e.tag||e.children.some(Ra))}function Fa(e,t){var n=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!n)return Ia(e,t,Fa,"null");if(e.for&&!e.forProcessed)return Da(e,t,Fa);var r=e.slotScope===ra?"":String(e.slotScope),i="function("+r+"){return "+("template"===e.tag?e.if&&n?"("+e.if+")?"+(Ba(e,t)||"undefined")+":undefined":Ba(e,t)||"undefined":Na(e,t))+"}",o=r?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+i+o+"}"}function Ba(e,t,n,r,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?t.maybeComponent(a)?",1":",0":"";return""+(r||Na)(a,t)+s}var c=n?function(e,t){for(var n=0,r=0;r]*>)","i")),p=e.replace(f,(function(e,n,r){return u=r.length,Oo(l)||"noscript"===l||(n=n.replace(//g,"$1").replace(//g,"$1")),Mo(l,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""}));c+=e.length-p.length,e=p,k(l,c-u,c)}else{var d=e.indexOf("<");if(0===d){if(ko.test(e)){var v=e.indexOf("--\x3e");if(v>=0){t.shouldKeepComment&&t.comment(e.substring(4,v),c,c+v+3),$(v+3);continue}}if(So.test(e)){var h=e.indexOf("]>");if(h>=0){$(h+2);continue}}var m=e.match(Ao);if(m){$(m[0].length);continue}var g=e.match(Co);if(g){var y=c;$(g[0].length),k(g[1],y,c);continue}var _=C();if(_){A(_),Mo(_.tagName,e)&&$(1);continue}}var b=void 0,w=void 0,x=void 0;if(d>=0){for(w=e.slice(d);!(Co.test(w)||xo.test(w)||ko.test(w)||So.test(w)||(x=w.indexOf("<",1))<0);)d+=x,w=e.slice(d);b=e.substring(0,d)}d<0&&(b=e),b&&$(b.length),t.chars&&b&&t.chars(b,c-b.length,c)}if(e===n){t.chars&&t.chars(e);break}}function $(t){c+=t,e=e.substring(t)}function C(){var t=e.match(xo);if(t){var n,r,i={tagName:t[1],attrs:[],start:c};for($(t[0].length);!(n=e.match($o))&&(r=e.match(_o)||e.match(yo));)r.start=c,$(r[0].length),r.end=c,i.attrs.push(r);if(n)return i.unarySlash=n[1],$(n[0].length),i.end=c,i}}function A(e){var n=e.tagName,c=e.unarySlash;o&&("p"===r&&go(n)&&k(r),s(n)&&r===n&&k(n));for(var u=a(n)||!!c,l=e.attrs.length,f=new Array(l),p=0;p=0&&i[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=i.length-1;u>=a;u--)t.end&&t.end(i[u].tag,n,o);i.length=a,r=a&&i[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,o):"p"===s&&(t.start&&t.start(e,[],!1,n,o),t.end&&t.end(e,n,o))}k()}(e,{warn:Do,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,o,a,l,f){var p=r&&r.ns||zo(e);G&&"svg"===p&&(o=function(e){for(var t=[],n=0;nc&&(s.push(o=e.slice(c,i)),a.push(JSON.stringify(o)));var u=$r(r[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),c=i+r[0].length}return c':'
',Wa.innerHTML.indexOf(" ")>0}var Ya=!!K&&Za(!1),Qa=!!K&&Za(!0),es=w((function(e){var t=Xn(e);return t&&t.innerHTML})),ts=xn.prototype.$mount;xn.prototype.$mount=function(e,t){if((e=e&&Xn(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=es(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(r){var i=Ga(r,{outputSourceRange:!1,shouldDecodeNewlines:Ya,shouldDecodeNewlinesForHref:Qa,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return ts.call(this,e,t)},xn.compile=Ga,e.exports=xn}).call(this,n(1),n(31).setImmediate)},function(e,t,n){(function(e){var r=void 0!==e&&e||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function o(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new o(i.call(setTimeout,r,arguments),clearTimeout)},t.setInterval=function(){return new o(i.call(setInterval,r,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},o.prototype.unref=o.prototype.ref=function(){},o.prototype.close=function(){this._clearFn.call(r,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},n(32),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(1))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var r,i,o,a,s,c=1,u={},l=!1,f=e.document,p=Object.getPrototypeOf&&Object.getPrototypeOf(e);p=p&&p.setTimeout?p:e,"[object process]"==={}.toString.call(e.process)?r=function(e){t.nextTick((function(){v(e)}))}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((o=new MessageChannel).port1.onmessage=function(e){v(e.data)},r=function(e){o.port2.postMessage(e)}):f&&"onreadystatechange"in f.createElement("script")?(i=f.documentElement,r=function(e){var t=f.createElement("script");t.onreadystatechange=function(){v(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):r=function(e){setTimeout(v,0,e)}:(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&v(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),r=function(t){e.postMessage(a+t,"*")}),p.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1?"s":""},showClass:function(){return this.title||this.subtitle?" bandwagon-show":""}}},(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{class:this.classSnackbar+e.showClass()},[n("div",{class:this.classMessage},[n(""!=this.url?"a":"span",{tag:"component",attrs:{id:"bandwagon-link",href:this.url||""}},[n("p",{class:this.classTitle},[e._v(e._s(e.title))]),e._v(" "),n("p",{class:this.classSubtitle},[e._v(e._s(e.subtitle))]),e._v(" "),n("p",{class:this.classTime},[e._v(e._s(e.timeAgo()))])])],1)])}),[],!1,null,null,null).exports;i.a.component("bandwagon-renderer",s),new i.a({el:"#bandwagon"})}]); -------------------------------------------------------------------------------- /public/app.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue.js v2.6.12 3 | * (c) 2014-2020 Evan You 4 | * Released under the MIT License. 5 | */ 6 | -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/app.js": "/app.js" 3 | } 4 | -------------------------------------------------------------------------------- /resources/js/BandwagonRenderer.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 122 | -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import BandwagonRenderer from './BandwagonRenderer' 3 | 4 | Vue.component('bandwagon-renderer', BandwagonRenderer); 5 | 6 | new Vue({ 7 | el: '#bandwagon', 8 | }); -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bndwgn/laravel-bandwagon/357b304f4a044708c3b6d5ef141548af51554790/resources/views/.gitkeep -------------------------------------------------------------------------------- /resources/views/renderer.blade.php: -------------------------------------------------------------------------------- 1 | 85 |
86 | 93 |
94 | 97 | -------------------------------------------------------------------------------- /src/Bandwagon.php: -------------------------------------------------------------------------------- 1 | config('bandwagon.delay'), 41 | 'display' => config('bandwagon.display'), 42 | 'enabled' => config('bandwagon.enabled'), 43 | 'poll' => config('bandwagon.poll'), 44 | 'path' => config('bandwagon.path'), 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/BandwagonFacade.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 16 | $this->publishes([ 17 | __DIR__ . '/../config/bandwagon.php' => config_path('bandwagon.php'), 18 | ], 'config'); 19 | 20 | $this->publishes([ 21 | __DIR__ . '/../resources/views' => base_path('resources/views/vendor/bandwagon'), 22 | ], 'views'); 23 | 24 | $migrationFileName = 'create_bandwagon_events_table.php'; 25 | if (! $this->migrationFileExists($migrationFileName)) { 26 | $this->publishes([ 27 | __DIR__ . "/../database/migrations/{$migrationFileName}.stub" => database_path('migrations/' . date('Y_m_d_His', time()) . '_' . $migrationFileName), 28 | ], 'migrations'); 29 | } 30 | 31 | $this->commands([ 32 | BandwagonCleanup::class, 33 | ]); 34 | 35 | $this->publishes([ 36 | __DIR__.'/../public' => public_path('vendor/bandwagon'), 37 | ], 'bandwagon-assets'); 38 | } 39 | 40 | $this->registerRoutes(); 41 | $this->registerProviders(); 42 | $this->loadComponents(); 43 | 44 | $this->loadViewsFrom(__DIR__ . '/../resources/views', 'bandwagon'); 45 | } 46 | 47 | public function register() 48 | { 49 | $this->mergeConfigFrom(__DIR__ . '/../config/bandwagon.php', 'bandwagon'); 50 | } 51 | 52 | public static function migrationFileExists(string $migrationFileName): bool 53 | { 54 | $len = strlen($migrationFileName); 55 | foreach (glob(database_path("migrations/*.php")) as $filename) { 56 | if ((substr($filename, -$len) === $migrationFileName)) { 57 | return true; 58 | } 59 | } 60 | 61 | return false; 62 | } 63 | 64 | protected function loadComponents() 65 | { 66 | $this->loadViewComponentsAs('bandwagon', [ 67 | Renderer::class, 68 | ]); 69 | } 70 | 71 | /** 72 | * Register the package routes. 73 | * 74 | * @return void 75 | */ 76 | private function registerRoutes() 77 | { 78 | Route::group($this->routeConfiguration(), function () { 79 | $this->loadRoutesFrom(__DIR__.'/Http/routes.php'); 80 | }); 81 | } 82 | 83 | /** 84 | * Register the package providers 85 | * 86 | * @return void 87 | */ 88 | protected function registerProviders() 89 | { 90 | $this->app->register(EventServiceProvider::class); 91 | } 92 | 93 | /** 94 | * Get the Bandwagon route group configuration array. 95 | * 96 | * @return array 97 | */ 98 | private function routeConfiguration() 99 | { 100 | return [ 101 | 'domain' => config('bandwagon.domain', null), 102 | 'namespace' => 'Bndwgn\Bandwagon\Http\Controllers', 103 | 'prefix' => config('bandwagon.path'), 104 | ]; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Commands/BandwagonCleanup.php: -------------------------------------------------------------------------------- 1 | comment('Removing old Bandwagon events...'); 17 | 18 | $time = time() - config('bandwagon.cleanup.olderthan'); 19 | BandwagonEvent::where('event_at', '<', $time)->delete(); 20 | 21 | $this->comment('All done'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Events/BandwagonEventCreated.php: -------------------------------------------------------------------------------- 1 | title = $title; 47 | $this->subtitle = $subtitle; 48 | $this->ip = $ip; 49 | $this->url = $url; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Http/Controllers/EventsController.php: -------------------------------------------------------------------------------- 1 | ', $request->query('since', $oldest)) 16 | ->where('ip', '!=', $request->ip()) 17 | ->orderBy('created_at', 'desc') 18 | ->first(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Http/routes.php: -------------------------------------------------------------------------------- 1 | $event->title, 14 | 'subtitle' => $event->subtitle, 15 | 'ip' => $event->ip, 16 | 'url' => $event->url, 17 | 'event_at' => time(), 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Models/BandwagonEvent.php: -------------------------------------------------------------------------------- 1 | [ 13 | RecordBandwagonEvent::class, 14 | ], 15 | ]; 16 | 17 | /** 18 | * Register any events for your application. 19 | * 20 | * @return void 21 | */ 22 | public function boot() 23 | { 24 | parent::boot(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/View/Components/Renderer.php: -------------------------------------------------------------------------------- 1 | classSnackbar = $classSnackbar; 33 | $this->classMessage = $classMessage; 34 | $this->classTitle = $classTitle; 35 | $this->classSubtitle = $classSubtitle; 36 | $this->classTime = $classTime; 37 | } 38 | 39 | /** 40 | * Get the view / contents that represent the component. 41 | * 42 | * @return \Illuminate\Contracts\View\View|string 43 | */ 44 | public function render() 45 | { 46 | return view('vendor.bandwagon.renderer', [ 47 | 'bandwagonScriptVariables' => Bandwagon::scriptVariables(), 48 | ]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix'); 2 | 3 | mix.js('resources/js/app.js', 'public') 4 | .setPublicPath('public'); --------------------------------------------------------------------------------