├── .gitignore ├── .phpunit.result.cache ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── docs ├── .gitattributes ├── .gitignore ├── .gitkeep ├── LICENSE ├── README.md ├── css │ ├── animate.css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── custom.css │ ├── prettyPhoto.css │ ├── stroke.css │ └── style.css ├── fonts │ ├── Stroke-Gap-Icons.eot │ ├── Stroke-Gap-Icons.svg │ ├── Stroke-Gap-Icons.ttf │ ├── Stroke-Gap-Icons.woff │ ├── font-awesome-4.3.0 │ │ ├── css │ │ │ ├── font-awesome.css │ │ │ └── font-awesome.min.css │ │ └── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── lanenar_Lane.eot │ ├── lanenar_Lane.svg │ ├── lanenar_Lane.ttf │ └── lanenar_Lane.woff ├── images │ ├── dummy.png │ ├── favicon.ico │ ├── prettyPhoto │ │ ├── dark_rounded │ │ │ ├── btnNext.png │ │ │ ├── btnPrevious.png │ │ │ ├── contentPattern.png │ │ │ ├── default_thumbnail.gif │ │ │ ├── loader.gif │ │ │ └── sprite.png │ │ ├── dark_square │ │ │ ├── btnNext.png │ │ │ ├── btnPrevious.png │ │ │ ├── contentPattern.png │ │ │ ├── default_thumbnail.gif │ │ │ ├── loader.gif │ │ │ └── sprite.png │ │ ├── default │ │ │ ├── default_thumb.png │ │ │ ├── loader.gif │ │ │ ├── sprite.png │ │ │ ├── sprite_next.png │ │ │ ├── sprite_prev.png │ │ │ ├── sprite_x.png │ │ │ └── sprite_y.png │ │ ├── facebook │ │ │ ├── btnNext.png │ │ │ ├── btnPrevious.png │ │ │ ├── contentPatternBottom.png │ │ │ ├── contentPatternLeft.png │ │ │ ├── contentPatternRight.png │ │ │ ├── contentPatternTop.png │ │ │ ├── default_thumbnail.gif │ │ │ ├── loader.gif │ │ │ └── sprite.png │ │ ├── light_rounded │ │ │ ├── btnNext.png │ │ │ ├── btnPrevious.png │ │ │ ├── default_thumbnail.gif │ │ │ ├── loader.gif │ │ │ └── sprite.png │ │ └── light_square │ │ │ ├── btnNext.png │ │ │ ├── btnPrevious.png │ │ │ ├── default_thumbnail.gif │ │ │ ├── loader.gif │ │ │ └── sprite.png │ └── upload │ │ ├── bg.png │ │ ├── plugin1.png │ │ ├── plugin2.png │ │ ├── plugin3.png │ │ └── thumbnail.png ├── index.html └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── custom.js │ ├── jquery.fitvids.js │ ├── jquery.min.js │ ├── jquery.min.map │ ├── jquery.prettyPhoto.js │ ├── main.js │ ├── retina.js │ ├── syntax-highlighter │ ├── scripts │ │ ├── shAutoloader.js │ │ ├── shBrushAS3.js │ │ ├── shBrushAppleScript.js │ │ ├── shBrushBash.js │ │ ├── shBrushCSharp.js │ │ ├── shBrushColdFusion.js │ │ ├── shBrushCpp.js │ │ ├── shBrushCss.js │ │ ├── shBrushDelphi.js │ │ ├── shBrushDiff.js │ │ ├── shBrushErlang.js │ │ ├── shBrushGroovy.js │ │ ├── shBrushJScript.js │ │ ├── shBrushJava.js │ │ ├── shBrushJavaFX.js │ │ ├── shBrushPerl.js │ │ ├── shBrushPhp.js │ │ ├── shBrushPlain.js │ │ ├── shBrushPowerShell.js │ │ ├── shBrushPython.js │ │ ├── shBrushRuby.js │ │ ├── shBrushSass.js │ │ ├── shBrushScala.js │ │ ├── shBrushSql.js │ │ ├── shBrushVb.js │ │ ├── shBrushXml.js │ │ ├── shCore.js │ │ └── shLegacy.js │ └── styles │ │ ├── shCore.css │ │ ├── shCoreDefault.css │ │ ├── shCoreDjango.css │ │ ├── shCoreEclipse.css │ │ ├── shCoreEmacs.css │ │ ├── shCoreFadeToGrey.css │ │ ├── shCoreMDUltra.css │ │ ├── shCoreMidnight.css │ │ ├── shCoreRDark.css │ │ ├── shThemeDefault.css │ │ ├── shThemeDjango.css │ │ ├── shThemeEclipse.css │ │ ├── shThemeEmacs.css │ │ ├── shThemeFadeToGrey.css │ │ ├── shThemeMDUltra.css │ │ ├── shThemeMidnight.css │ │ └── shThemeRDark.css │ └── wow.js ├── phpunit.xml ├── src ├── Achievement.php ├── AchievementChain.php ├── AchievementsServiceProvider.php ├── Achiever.php ├── CanAchieve.php ├── Console │ ├── AchievementChainMakeCommand.php │ ├── AchievementMakeCommand.php │ └── stubs │ │ ├── achievement_chain_class.stub │ │ └── achievement_class.stub ├── EntityRelationsAchievements.php ├── Event │ ├── Progress.php │ └── Unlocked.php ├── Migrations │ └── 0000_00_00_000000_create_achievements_tables.php ├── Model │ ├── AchievementDetails.php │ └── AchievementProgress.php ├── RoutesAchievements.php └── config │ └── achievements.php └── tests ├── .gitkeep ├── AchievementChains └── PostChain.php ├── AchievementTest.php ├── Achievements ├── FiftyPosts.php ├── FirstPost.php └── TenPosts.php ├── ChainAchievementTest.php ├── DBTestCase.php └── Model └── User.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | node_modules/ 3 | composer.lock 4 | .idea 5 | 6 | # Laravel 4 specific 7 | bootstrap/compiled.php 8 | app/storage/ 9 | 10 | # Laravel 5 & Lumen specific 11 | bootstrap/cache/ 12 | .env.*.php 13 | .env.php 14 | .env 15 | 16 | # Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer 17 | .rocketeer/ 18 | -------------------------------------------------------------------------------- /.phpunit.result.cache: -------------------------------------------------------------------------------- 1 | C:37:"PHPUnit\Runner\DefaultTestResultCache":675:{a:2:{s:7:"defects";a:0:{}s:5:"times";a:10:{s:40:"Gstt\Tests\AchievementTest::testUnlocked";d:0.17;s:37:"Gstt\Tests\AchievementTest::testSetup";d:0.012;s:38:"Gstt\Tests\AchievementTest::testUnlock";d:0.028;s:40:"Gstt\Tests\AchievementTest::testProgress";d:0.031;s:55:"Gstt\Tests\AchievementTest::testRelationsLockedUnsynced";d:0.026;s:53:"Gstt\Tests\AchievementTest::testRelationsLockedSynced";d:0.026;s:52:"Gstt\Tests\AchievementTest::testUnlockedWithMorphMap";d:0.012;s:48:"Gstt\Tests\AchievementTest::testAchieverMorphMap";d:0.013;s:63:"Gstt\Tests\AchievementTest::testAchievementProgressMapToDetails";d:0.013;s:45:"Gstt\Tests\ChainAchievementTest::testProgress";d:0.071;}}} -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.2 4 | before_install: 5 | - composer self-update 6 | - composer install --no-interaction 7 | script: 8 | - vendor/bin/phpunit --version 9 | - vendor/bin/phpunit 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Gabriel Simonetti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gstt/laravel-achievements", 3 | "description": "Achievements for Laravel 5.3+", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Gabriel Simonetti", 8 | "email": "simonettigo@gmail.com" 9 | } 10 | ], 11 | "autoload": { 12 | "classmap": [ 13 | "src/Migrations" 14 | ], 15 | "psr-4": { 16 | "Gstt\\Achievements\\": "src/", 17 | "Gstt\\Tests\\": "tests/" 18 | } 19 | }, 20 | "require": { 21 | "ramsey/uuid": "^3.5" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "^8", 25 | "laravel/laravel": "^6.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/.gitkeep -------------------------------------------------------------------------------- /docs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Surjith S M 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Laravel Achievements Documentation 2 | The new documentation for Laravel Achievements. 3 | 4 | ## Credits 5 | [http://surjithctly.github.io/documentation-html-template/](http://surjithctly.github.io/documentation-html-template/) 6 | 7 | This is a modified version of [Template Visual's](http://themeforest.net/user/templatevisual?ref=surjithctly&utm_source=github_surjithctly_docs) Documentation : Actual Documentation Link : http://goo.gl/RVwdHE -------------------------------------------------------------------------------- /docs/css/custom.css: -------------------------------------------------------------------------------- 1 | /*Docs*/ 2 | 3 | .body { 4 | position: relative; 5 | } 6 | 7 | .sub-title { 8 | font-size: 30px; 9 | margin-top: 50px; 10 | margin-bottom: 10px; 11 | padding-bottom: 0; 12 | } 13 | 14 | .sub-title a { 15 | font-size: 16px; 16 | } 17 | 18 | .section.docs-heading { 19 | padding: 60px 0; 20 | } 21 | 22 | .affix { 23 | position: static; 24 | } 25 | 26 | @media (min-width: 992px) { 27 | .affix, 28 | .affix-bottom { 29 | width: 213px 30 | } 31 | .affix { 32 | position: fixed; 33 | top: 20px 34 | } 35 | .affix-bottom { 36 | position: absolute 37 | } 38 | .affix .bs-docs-sidenav, 39 | .affix-bottom .bs-docs-sidenav { 40 | margin-top: 0; 41 | margin-bottom: 0 42 | } 43 | } 44 | 45 | @media (min-width: 1200px) { 46 | .affix, 47 | .affix-bottom { 48 | width: 263px 49 | } 50 | } 51 | 52 | /* SIDEBAR */ 53 | 54 | @media (min-width: 768px) { 55 | .docs-sidebar { 56 | padding-top: 20px; 57 | padding-left: 20px 58 | } 59 | } 60 | 61 | /* all links */ 62 | .docs-sidebar .nav>li>a { 63 | color: #111; 64 | border-left: 2px solid transparent; 65 | padding: 0 20px; 66 | font-size: 15px; 67 | font-weight: 400; 68 | } 69 | 70 | /* nested links */ 71 | .docs-sidebar .nav .nav>li>a { 72 | padding-left: 40px; 73 | font-size: 14px; 74 | } 75 | 76 | /* hover links */ 77 | .docs-sidebar .nav>li:not(.active)>a:hover { 78 | color: #0E97EE; 79 | text-decoration: none; 80 | background-color: transparent; 81 | border-left-width: 1px; 82 | border-left-color: #0E97EE; 83 | } 84 | /* focus links */ 85 | .docs-sidebar .nav>li>a:focus { 86 | text-decoration: none; 87 | background-color: transparent; 88 | } 89 | /* active links */ 90 | .docs-sidebar .nav>.active>a { 91 | color: #0E97EE; 92 | text-decoration: none; 93 | background-color: transparent; 94 | border-left-color: #0E97EE; 95 | } 96 | /* all active links */ 97 | .docs-sidebar .nav>.active>a, 98 | .docs-sidebar .nav>.active:hover>a, 99 | .docs-sidebar .nav>.active:focus>a { 100 | font-weight: 700; 101 | } 102 | /* nested active links */ 103 | .docs-sidebar .nav .nav>.active>a, 104 | .docs-sidebar .nav .nav>.active:hover>a, 105 | .docs-sidebar .nav .nav>.active:focus>a { 106 | font-weight: 500; 107 | } 108 | 109 | @media (min-width: 992px) { 110 | .docs-sidebar .nav ul { 111 | display: none; 112 | padding-bottom: 10px; 113 | } 114 | .docs-sidebar .nav>.active>ul { 115 | display: block 116 | } 117 | } 118 | /*Syntax Highlighter : Sublime Theme */ 119 | 120 | .syntaxhighlighter { 121 | background-color: #2b303b !important; 122 | padding: 15px 0; 123 | margin: 2em 0 1em 0 !important; 124 | } 125 | 126 | .syntaxhighlighter a, 127 | .syntaxhighlighter div, 128 | .syntaxhighlighter code, 129 | .syntaxhighlighter table, 130 | .syntaxhighlighter table td, 131 | .syntaxhighlighter table tr, 132 | .syntaxhighlighter table tbody, 133 | .syntaxhighlighter table thead, 134 | .syntaxhighlighter table caption, 135 | .syntaxhighlighter textarea { 136 | line-height: 1.3em !important; 137 | } 138 | 139 | .syntaxhighlighter .line.alt1 { 140 | background-color: #2b303b !important; 141 | } 142 | 143 | .syntaxhighlighter .line.alt2 { 144 | background-color: #2b303b !important; 145 | } 146 | 147 | .syntaxhighlighter .string, 148 | .syntaxhighlighter .string a { 149 | color: #90be8c !important; 150 | } 151 | 152 | .syntaxhighlighter .color1, 153 | .syntaxhighlighter .color1 a { 154 | color: #d08770 !important; 155 | } 156 | 157 | .syntaxhighlighter .plain, 158 | .syntaxhighlighter .plain a { 159 | color: #c0c5ce !important; 160 | } 161 | 162 | .syntaxhighlighter .keyword { 163 | color: #bf616a !important; 164 | } 165 | 166 | .syntaxhighlighter .gutter { 167 | color: #757a84 !important; 168 | } 169 | 170 | .syntaxhighlighter .line.highlighted.alt1, 171 | .syntaxhighlighter .line.highlighted.alt2 { 172 | background-color: #333E49 !important; 173 | } 174 | 175 | .syntaxhighlighter .gutter .line.highlighted { 176 | background-color: #343d46 !important; 177 | color: #757a84 !important; 178 | } 179 | 180 | .syntaxhighlighter .value { 181 | color: #96b5b4 !important; 182 | } 183 | /*css*/ 184 | /* .syntaxhighlighter .css.plain, 185 | .syntaxhighlighter .css.plain a { 186 | color: #d08770 !important; 187 | } 188 | 189 | .syntaxhighlighter .css.keyword { 190 | color: #c0c5ce !important; 191 | }*/ 192 | 193 | .syntaxhighlighter .color3, 194 | .syntaxhighlighter .color3 a { 195 | color: #b48ead !important; 196 | } 197 | /*js*/ 198 | 199 | .syntaxhighlighter .js.keyword { 200 | color: #b48ead !important; 201 | } 202 | 203 | ul.list{ 204 | list-style: circle; 205 | padding-left: 1em; 206 | } 207 | 208 | ul.list li{ 209 | line-height: 20px; 210 | } 211 | 212 | .syntaxhighlighter td.gutter{ 213 | vertical-align: middle !important; 214 | } -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------- 2 | 3 | File Name: style.css 4 | Template Name: Documentation 5 | Created By: Surjith S M 6 | http://themeforest.net/user/surjithctly 7 | 8 | 1. IMPORT 9 | 2. SKELETON 10 | 11 | ------------------------------------------------------- */ 12 | /* ---------------------------------------------------- 13 | IMPORT 14 | ------------------------------------------------------- */ 15 | 16 | @import url(http://fonts.googleapis.com/css?family=Lato:400,100,100italic,300,300italic,400italic,700,700italic,900,900italic); 17 | /* ---------------------------------------------------- 18 | SKELETON 19 | ------------------------------------------------------- */ 20 | 21 | body { 22 | background: #fff; 23 | color: #777777; 24 | font-size: 14px; 25 | line-height: 31px; 26 | letter-spacing: 0; 27 | font-weight: 400; 28 | padding: 0; 29 | } 30 | 31 | h1, 32 | h2, 33 | h3, 34 | h4, 35 | h5, 36 | h6 { 37 | letter-spacing: 0px; 38 | font-weight: normal; 39 | position: relative; 40 | padding: 0 0 10px 0; 41 | font-weight: normal; 42 | line-height: 1.8; 43 | color: #242424; 44 | } 45 | 46 | h1 { 47 | font-size: 22px; 48 | } 49 | 50 | h2 { 51 | font-size: 20px; 52 | } 53 | 54 | h3 { 55 | font-size: 18px; 56 | } 57 | 58 | h4 { 59 | font-size: 21px; 60 | } 61 | 62 | h5 { 63 | font-size: 14px; 64 | } 65 | 66 | h6 { 67 | font-size: 14px; 68 | } 69 | 70 | h1 a, 71 | h2 a, 72 | h3 a, 73 | h4 a, 74 | h5 a, 75 | h6 a { 76 | text-decoration: none !important; 77 | } 78 | 79 | p { 80 | font-size: 15px; 81 | font-weight: 400; 82 | line-height: 26px; 83 | letter-spacing: 0; 84 | margin-top: 15px; 85 | } 86 | 87 | img.aligncenter { 88 | display: block; 89 | text-align: center; 90 | display: block; 91 | margin: 0 auto 20px auto; 92 | padding: 0px; 93 | border: 0px; 94 | background: none; 95 | } 96 | 97 | ul { 98 | padding: 0; 99 | list-style: none; 100 | } 101 | 102 | ol li ul li { 103 | padding-left: 20px; 104 | } 105 | 106 | img.alignleft { 107 | float: left; 108 | margin: 6px 20px 6px 0; 109 | display: inline; 110 | border: 0px; 111 | background: none; 112 | padding: 0; 113 | display: block; 114 | } 115 | 116 | img.alignright { 117 | padding: 0; 118 | float: right; 119 | margin: 6px 0 6px 20px; 120 | border: 0px; 121 | display: block; 122 | background: none; 123 | } 124 | 125 | blockquote { 126 | font-size: 16px; 127 | line-height: 32px; 128 | font-family: 'Droid Serif', Georgia, "Times New Roman", serif; 129 | font-weight: normal; 130 | font-style: italic; 131 | position: relative; 132 | width: auto; 133 | } 134 | 135 | blockquote small { 136 | display: block; 137 | margin-top: 20px; 138 | } 139 | 140 | pre { 141 | line-height: 18px; 142 | margin-bottom: 18px; 143 | } 144 | 145 | .btn, 146 | a { 147 | outline: 0 !important; 148 | text-decoration: none !important; 149 | } 150 | 151 | ins { 152 | text-decoration: none; 153 | } 154 | 155 | sup { 156 | bottom: 1ex; 157 | } 158 | 159 | sub { 160 | top: .5ex; 161 | } 162 | 163 | p { 164 | padding: 0 0 20px 0; 165 | } 166 | 167 | .check li:before { 168 | content: "\f00c"; 169 | font-family: "FontAwesome"; 170 | font-size: 16px; 171 | left: 0; 172 | color: #242424; 173 | padding-right: 5px; 174 | position: relative; 175 | top: 2px; 176 | } 177 | 178 | .check li { 179 | font-size: 14px; 180 | list-style: none; 181 | margin-bottom: 4px; 182 | } 183 | 184 | .check { 185 | margin-left: 0; 186 | padding-left: 0 187 | } 188 | 189 | .section { 190 | padding: 180px 0; 191 | position: relative; 192 | display: block; 193 | } 194 | 195 | .white { 196 | background-color: #ffffff; 197 | } 198 | 199 | strong { 200 | color: #030303; 201 | } 202 | 203 | .grey { 204 | background-color: #f3f3f3; 205 | } 206 | 207 | .big-title h1 { 208 | font-size: 48px; 209 | font-weight: 300; 210 | margin-bottom: 0; 211 | text-transform: capitalize; 212 | padding-bottom: 0; 213 | } 214 | 215 | .dark-text { 216 | font-size: 4.125rem; 217 | line-height: 4.063rem; 218 | font-weight: 300; 219 | } 220 | 221 | h4 a { 222 | font-size: 14px; 223 | font-weight: bold; 224 | } 225 | 226 | .dark-text a { 227 | font-size: 14px; 228 | font-weight: bold; 229 | padding-left: 20px; 230 | } 231 | 232 | .dark-text hr { 233 | width: 130px; 234 | margin-left: 0; 235 | margin-top: 40px; 236 | border-width: 1px; 237 | border-color: #030303; 238 | } 239 | 240 | .intro { 241 | padding: 20px; 242 | } 243 | 244 | .intro1 { 245 | margin-top: 22px; 246 | padding: 30px 0px; 247 | background-color: #f3f3f3; 248 | } 249 | 250 | .intro1 li { 251 | list-style: none; 252 | } 253 | 254 | .intro1 a { 255 | color: #0E97EE !important; 256 | } 257 | 258 | .intro a { 259 | color: #030303; 260 | font-weight: 400; 261 | font-size: 16px; 262 | } 263 | 264 | .lead { 265 | text-transform: capitalize; 266 | } 267 | 268 | .left-align img { 269 | margin-top: 20px; 270 | } 271 | 272 | .drop-caps p:first-child::first-letter { 273 | color: #030303; 274 | display: block; 275 | float: left; 276 | font-size: 75px; 277 | line-height: 60px; 278 | text-transform: uppercase; 279 | margin-right: 10px; 280 | margin-top: 5px; 281 | padding: 4px; 282 | } 283 | 284 | .drop-caps.full p:first-child::first-letter { 285 | background: #030303 none repeat scroll 0 0 !important; 286 | color: #ffffff; 287 | margin-right: 15px; 288 | padding: 20px; 289 | font-size: 36px; 290 | font-family: Georgia; 291 | border-radius: 5px; 292 | } 293 | 294 | mark { 295 | background-color: #0E97EE; 296 | color: #ffffff; 297 | padding: 0 10px; 298 | } 299 | 300 | a:focus, 301 | a:hover { 302 | color: #0E97EE; 303 | } 304 | 305 | .intro2 i { 306 | padding-right: 10px; 307 | font-size: 21px; 308 | } 309 | 310 | .intro2 { 311 | background-color: #0E97EE; 312 | border: 2px solid #2187BB; 313 | border-radius: 5px; 314 | color: #ffffff; 315 | font-style: italic; 316 | padding: 10px 20px; 317 | } 318 | 319 | .intro2 p { 320 | padding: 0; 321 | } 322 | 323 | .intro2 a { 324 | color: #ffffff; 325 | font-weight: bold; 326 | } 327 | 328 | .intro1 ul { 329 | padding: 0 30px; 330 | } 331 | 332 | .btn-primary { 333 | background-color: #0E97EE; 334 | border: 1px solid #2187BB; 335 | } 336 | 337 | .btn-primary:hover { 338 | background-color: #2187BB; 339 | border: 1px solid #0E97EE; 340 | } 341 | 342 | .btn-info { 343 | background-color: #545454; 344 | border: 1px solid #656565; 345 | } 346 | 347 | .btn-info:hover { 348 | background-color: #656565; 349 | border: 1px solid #545454; 350 | } 351 | 352 | .navbar-inverse .navbar-nav > .active > a, 353 | .navbar-inverse .navbar-nav > .active > a:hover, 354 | .navbar-inverse .navbar-nav > .active > a:focus { 355 | background-color: #0E97EE; 356 | color: #fff; 357 | } 358 | 359 | .navbar-inverse { 360 | background-color: #030303; 361 | border: 0; 362 | } 363 | 364 | .navbar-inverse li.btn { 365 | border-radius: 0 !important; 366 | padding: 0; 367 | } 368 | 369 | .navbar-inverse li.btn a { 370 | color: #ffffff !important; 371 | } 372 | -------------------------------------------------------------------------------- /docs/fonts/Stroke-Gap-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/Stroke-Gap-Icons.eot -------------------------------------------------------------------------------- /docs/fonts/Stroke-Gap-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/Stroke-Gap-Icons.ttf -------------------------------------------------------------------------------- /docs/fonts/Stroke-Gap-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/Stroke-Gap-Icons.woff -------------------------------------------------------------------------------- /docs/fonts/font-awesome-4.3.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/font-awesome-4.3.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/fonts/lanenar_Lane.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/lanenar_Lane.eot -------------------------------------------------------------------------------- /docs/fonts/lanenar_Lane.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/lanenar_Lane.ttf -------------------------------------------------------------------------------- /docs/fonts/lanenar_Lane.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/fonts/lanenar_Lane.woff -------------------------------------------------------------------------------- /docs/images/dummy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/dummy.png -------------------------------------------------------------------------------- /docs/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/favicon.ico -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_rounded/btnNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_rounded/btnNext.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_rounded/btnPrevious.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_rounded/btnPrevious.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_rounded/contentPattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_rounded/contentPattern.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_rounded/default_thumbnail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_rounded/default_thumbnail.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_rounded/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_rounded/loader.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_rounded/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_rounded/sprite.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_square/btnNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_square/btnNext.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_square/btnPrevious.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_square/btnPrevious.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_square/contentPattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_square/contentPattern.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_square/default_thumbnail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_square/default_thumbnail.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_square/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_square/loader.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/dark_square/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/dark_square/sprite.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/default/default_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/default/default_thumb.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/default/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/default/loader.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/default/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/default/sprite.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/default/sprite_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/default/sprite_next.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/default/sprite_prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/default/sprite_prev.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/default/sprite_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/default/sprite_x.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/default/sprite_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/default/sprite_y.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/btnNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/btnNext.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/btnPrevious.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/btnPrevious.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/contentPatternBottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/contentPatternBottom.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/contentPatternLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/contentPatternLeft.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/contentPatternRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/contentPatternRight.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/contentPatternTop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/contentPatternTop.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/default_thumbnail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/default_thumbnail.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/loader.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/facebook/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/facebook/sprite.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_rounded/btnNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_rounded/btnNext.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_rounded/btnPrevious.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_rounded/btnPrevious.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_rounded/default_thumbnail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_rounded/default_thumbnail.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_rounded/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_rounded/loader.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_rounded/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_rounded/sprite.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_square/btnNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_square/btnNext.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_square/btnPrevious.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_square/btnPrevious.png -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_square/default_thumbnail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_square/default_thumbnail.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_square/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_square/loader.gif -------------------------------------------------------------------------------- /docs/images/prettyPhoto/light_square/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/prettyPhoto/light_square/sprite.png -------------------------------------------------------------------------------- /docs/images/upload/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/upload/bg.png -------------------------------------------------------------------------------- /docs/images/upload/plugin1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/upload/plugin1.png -------------------------------------------------------------------------------- /docs/images/upload/plugin2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/upload/plugin2.png -------------------------------------------------------------------------------- /docs/images/upload/plugin3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/upload/plugin3.png -------------------------------------------------------------------------------- /docs/images/upload/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/docs/images/upload/thumbnail.png -------------------------------------------------------------------------------- /docs/js/custom.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | "use strict"; 3 | 4 | /* ============================================== 5 | ANIMATION --> 6 | =============================================== */ 7 | 8 | new WOW({ 9 | boxClass: 'wow', // default 10 | animateClass: 'animated', // default 11 | offset: 0, // default 12 | mobile: true, // default 13 | live: true // default 14 | }).init(); 15 | 16 | /* ============================================== 17 | LIGHTBOX --> 18 | =============================================== */ 19 | 20 | jQuery('a[data-gal]').each(function() { 21 | jQuery(this).attr('rel', jQuery(this).data('gal')); 22 | }); 23 | jQuery("a[data-rel^='prettyPhoto']").prettyPhoto({ 24 | animationSpeed: 'slow', 25 | theme: 'light_square', 26 | slideshow: true, 27 | overlay_gallery: true, 28 | social_tools: false, 29 | deeplinking: false 30 | }); 31 | 32 | /* ============================================== 33 | SCROLL --> 34 | =============================================== */ 35 | 36 | $(function() { 37 | $('a[href*=#]:not([href=#])').click(function() { 38 | if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) { 39 | var target = $(this.hash); 40 | target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 41 | if (target.length) { 42 | $('html,body').animate({ 43 | scrollTop: target.offset().top 44 | }, 1000); 45 | return false; 46 | } 47 | } 48 | }); 49 | }); 50 | 51 | /* ============================================== 52 | SCROLLSPY --> 53 | =============================================== */ 54 | 55 | $('body').scrollspy({ 56 | target: '.docs-sidebar' 57 | }); 58 | 59 | $('[data-spy="scroll"]').each(function () { 60 | var $spy = $(this).scrollspy('refresh') 61 | }) 62 | 63 | /* ============================================== 64 | VIDEO FIX --> 65 | =============================================== */ 66 | 67 | $(document).ready(function() { 68 | // Target your .container, .wrapper, .post, etc. 69 | $(".media").fitVids(); 70 | }); 71 | 72 | /* ============================================== 73 | VIDEO FIX --> 74 | =============================================== */ 75 | 76 | $('.docs-sidebar>nav>li>a').click(function() { 77 | $('.docs-sidebar>nav>li').removeClass('active'); 78 | $(this).parent().addClass('active'); 79 | }); 80 | 81 | })(jQuery); 82 | -------------------------------------------------------------------------------- /docs/js/jquery.fitvids.js: -------------------------------------------------------------------------------- 1 | /*jshint browser:true */ 2 | /*! 3 | * FitVids 1.1 4 | * 5 | * Copyright 2013, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com 6 | * Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ 7 | * Released under the WTFPL license - http://sam.zoy.org/wtfpl/ 8 | * 9 | */ 10 | 11 | ;(function( $ ){ 12 | 13 | 'use strict'; 14 | 15 | $.fn.fitVids = function( options ) { 16 | var settings = { 17 | customSelector: null, 18 | ignore: null 19 | }; 20 | 21 | if(!document.getElementById('fit-vids-style')) { 22 | // appendStyles: https://github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.js 23 | var head = document.head || document.getElementsByTagName('head')[0]; 24 | var css = '.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}'; 25 | var div = document.createElement("div"); 26 | div.innerHTML = '
x
'; 27 | head.appendChild(div.childNodes[1]); 28 | } 29 | 30 | if ( options ) { 31 | $.extend( settings, options ); 32 | } 33 | 34 | return this.each(function(){ 35 | var selectors = [ 36 | 'iframe[src*="player.vimeo.com"]', 37 | 'iframe[src*="youtube.com"]', 38 | 'iframe[src*="youtube-nocookie.com"]', 39 | 'iframe[src*="kickstarter.com"][src*="video.html"]', 40 | 'object', 41 | 'embed' 42 | ]; 43 | 44 | if (settings.customSelector) { 45 | selectors.push(settings.customSelector); 46 | } 47 | 48 | var ignoreList = '.fitvidsignore'; 49 | 50 | if(settings.ignore) { 51 | ignoreList = ignoreList + ', ' + settings.ignore; 52 | } 53 | 54 | var $allVideos = $(this).find(selectors.join(',')); 55 | $allVideos = $allVideos.not('object object'); // SwfObj conflict patch 56 | $allVideos = $allVideos.not(ignoreList); // Disable FitVids on this video. 57 | 58 | $allVideos.each(function(count){ 59 | var $this = $(this); 60 | if($this.parents(ignoreList).length > 0) { 61 | return; // Disable FitVids on this video. 62 | } 63 | if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; } 64 | if ((!$this.css('height') && !$this.css('width')) && (isNaN($this.attr('height')) || isNaN($this.attr('width')))) 65 | { 66 | $this.attr('height', 9); 67 | $this.attr('width', 16); 68 | } 69 | var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(), 70 | width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(), 71 | aspectRatio = height / width; 72 | if(!$this.attr('id')){ 73 | var videoID = 'fitvid' + count; 74 | $this.attr('id', videoID); 75 | } 76 | $this.wrap('').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+'%'); 77 | $this.removeAttr('height').removeAttr('width'); 78 | }); 79 | }); 80 | }; 81 | // Works with either jQuery or Zepto 82 | })( window.jQuery || window.Zepto ); 83 | -------------------------------------------------------------------------------- /docs/js/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | SyntaxHighlighter.defaults['toolbar'] = false; 4 | SyntaxHighlighter.all(); 5 | 6 | }); 7 | -------------------------------------------------------------------------------- /docs/js/retina.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Retina.js v1.3.0 3 | * 4 | * Copyright 2014 Imulus, LLC 5 | * Released under the MIT license 6 | * 7 | * Retina.js is an open source script that makes it easy to serve 8 | * high-resolution images to devices with retina displays. 9 | */ 10 | 11 | (function() { 12 | var root = (typeof exports === 'undefined' ? window : exports); 13 | var config = { 14 | // An option to choose a suffix for 2x images 15 | retinaImageSuffix : '@2x', 16 | 17 | // Ensure Content-Type is an image before trying to load @2x image 18 | // https://github.com/imulus/retinajs/pull/45) 19 | check_mime_type: true, 20 | 21 | // Resize high-resolution images to original image's pixel dimensions 22 | // https://github.com/imulus/retinajs/issues/8 23 | force_original_dimensions: true 24 | }; 25 | 26 | function Retina() {} 27 | 28 | root.Retina = Retina; 29 | 30 | Retina.configure = function(options) { 31 | if (options === null) { 32 | options = {}; 33 | } 34 | 35 | for (var prop in options) { 36 | if (options.hasOwnProperty(prop)) { 37 | config[prop] = options[prop]; 38 | } 39 | } 40 | }; 41 | 42 | Retina.init = function(context) { 43 | if (context === null) { 44 | context = root; 45 | } 46 | 47 | var existing_onload = context.onload || function(){}; 48 | 49 | context.onload = function() { 50 | var images = document.getElementsByTagName('img'), retinaImages = [], i, image; 51 | for (i = 0; i < images.length; i += 1) { 52 | image = images[i]; 53 | if (!!!image.getAttributeNode('data-no-retina')) { 54 | retinaImages.push(new RetinaImage(image)); 55 | } 56 | } 57 | existing_onload(); 58 | }; 59 | }; 60 | 61 | Retina.isRetina = function(){ 62 | var mediaQuery = '(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 1.5dppx)'; 63 | 64 | if (root.devicePixelRatio > 1) { 65 | return true; 66 | } 67 | 68 | if (root.matchMedia && root.matchMedia(mediaQuery).matches) { 69 | return true; 70 | } 71 | 72 | return false; 73 | }; 74 | 75 | 76 | var regexMatch = /\.\w+$/; 77 | function suffixReplace (match) { 78 | return config.retinaImageSuffix + match; 79 | } 80 | 81 | function RetinaImagePath(path, at_2x_path) { 82 | this.path = path || ''; 83 | if (typeof at_2x_path !== 'undefined' && at_2x_path !== null) { 84 | this.at_2x_path = at_2x_path; 85 | this.perform_check = false; 86 | } else { 87 | if (undefined !== document.createElement) { 88 | var locationObject = document.createElement('a'); 89 | locationObject.href = this.path; 90 | locationObject.pathname = locationObject.pathname.replace(regexMatch, suffixReplace); 91 | this.at_2x_path = locationObject.href; 92 | } else { 93 | var parts = this.path.split('?'); 94 | parts[0] = parts[0].replace(regexMatch, suffixReplace); 95 | this.at_2x_path = parts.join('?'); 96 | } 97 | this.perform_check = true; 98 | } 99 | } 100 | 101 | root.RetinaImagePath = RetinaImagePath; 102 | 103 | RetinaImagePath.confirmed_paths = []; 104 | 105 | RetinaImagePath.prototype.is_external = function() { 106 | return !!(this.path.match(/^https?\:/i) && !this.path.match('//' + document.domain) ); 107 | }; 108 | 109 | RetinaImagePath.prototype.check_2x_variant = function(callback) { 110 | var http, that = this; 111 | if (this.is_external()) { 112 | return callback(false); 113 | } else if (!this.perform_check && typeof this.at_2x_path !== 'undefined' && this.at_2x_path !== null) { 114 | return callback(true); 115 | } else if (this.at_2x_path in RetinaImagePath.confirmed_paths) { 116 | return callback(true); 117 | } else { 118 | http = new XMLHttpRequest(); 119 | http.open('HEAD', this.at_2x_path); 120 | http.onreadystatechange = function() { 121 | if (http.readyState !== 4) { 122 | return callback(false); 123 | } 124 | 125 | if (http.status >= 200 && http.status <= 399) { 126 | if (config.check_mime_type) { 127 | var type = http.getResponseHeader('Content-Type'); 128 | if (type === null || !type.match(/^image/i)) { 129 | return callback(false); 130 | } 131 | } 132 | 133 | RetinaImagePath.confirmed_paths.push(that.at_2x_path); 134 | return callback(true); 135 | } else { 136 | return callback(false); 137 | } 138 | }; 139 | http.send(); 140 | } 141 | }; 142 | 143 | 144 | function RetinaImage(el) { 145 | this.el = el; 146 | this.path = new RetinaImagePath(this.el.getAttribute('src'), this.el.getAttribute('data-at2x')); 147 | var that = this; 148 | this.path.check_2x_variant(function(hasVariant) { 149 | if (hasVariant) { 150 | that.swap(); 151 | } 152 | }); 153 | } 154 | 155 | root.RetinaImage = RetinaImage; 156 | 157 | RetinaImage.prototype.swap = function(path) { 158 | if (typeof path === 'undefined') { 159 | path = this.path.at_2x_path; 160 | } 161 | 162 | var that = this; 163 | function load() { 164 | if (! that.el.complete) { 165 | setTimeout(load, 5); 166 | } else { 167 | if (config.force_original_dimensions) { 168 | that.el.setAttribute('width', that.el.offsetWidth); 169 | that.el.setAttribute('height', that.el.offsetHeight); 170 | } 171 | 172 | that.el.setAttribute('src', path); 173 | } 174 | } 175 | load(); 176 | }; 177 | 178 | 179 | if (Retina.isRetina()) { 180 | Retina.init(root); 181 | } 182 | })(); 183 | -------------------------------------------------------------------------------- /docs/js/syntax-highlighter/scripts/shAutoloader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(2(){1 h=5;h.I=2(){2 n(c,a){4(1 d=0;d\\\\w+)\\\\]$","U"),i=1,p=0;p2 | 10 | 23 | -------------------------------------------------------------------------------- /src/Achievement.php: -------------------------------------------------------------------------------- 1 | getModel(); 53 | } 54 | 55 | /** 56 | * Wrapper for AchievementDetail::all(); 57 | * Conveniently fetches all achievements stored in the database. 58 | */ 59 | public static function all(){ 60 | return AchievementDetails::all(); 61 | } 62 | 63 | /** 64 | * Gets the full class name. 65 | * 66 | * @return string 67 | */ 68 | public function getClassName() 69 | { 70 | return static::class; 71 | } 72 | 73 | /** 74 | * Gets the amount of points needed to unlock the achievement. 75 | * 76 | * @return int 77 | */ 78 | public function getPoints() 79 | { 80 | return $this->points; 81 | } 82 | 83 | /** 84 | * Gets the details class for this achievement. 85 | * 86 | * @return AchievementDetails 87 | */ 88 | public function getModel() 89 | { 90 | if(!is_null($this->modelAttr)){ 91 | return $this->modelAttr; 92 | } 93 | 94 | $model = AchievementDetails::where('class_name', $this->getClassName())->first(); 95 | 96 | if (is_null($model)) { 97 | $model = new AchievementDetails(); 98 | $model->class_name = $this->getClassName(); 99 | } 100 | 101 | if(config('achievements.auto_sync') || is_null($model->name)) { 102 | $model->name = $this->name; 103 | $model->description = $this->description; 104 | $model->points = $this->points; 105 | $model->secret = $this->secret; 106 | 107 | // Syncs 108 | $model->save(); 109 | } 110 | 111 | $this->modelAttr = $model; 112 | return $model; 113 | } 114 | 115 | /** 116 | * Adds a specified amount of points to the achievement. 117 | * 118 | * @param mixed $achiever The entity that will add progress to this achievement 119 | * @param int $points The amount of points to be added to this achievement 120 | */ 121 | public function addProgressToAchiever($achiever, $points = 1) 122 | { 123 | $progress = $this->getOrCreateProgressForAchiever($achiever); 124 | if (!$progress->isUnlocked()) { 125 | $progress->points = $progress->points + $points; 126 | $progress->save(); 127 | } 128 | } 129 | 130 | /** 131 | * Sets a specified amount of points to the achievement. 132 | * 133 | * @param mixed $achiever The entity that will add progress to this achievement 134 | * @param int $points The amount of points to be added to this achievement 135 | */ 136 | public function setProgressToAchiever($achiever, $points) 137 | { 138 | $progress = $this->getOrCreateProgressForAchiever($achiever); 139 | 140 | if (!$progress->isUnlocked()) { 141 | $progress->points = $points; 142 | $progress->save(); 143 | } 144 | } 145 | 146 | /** 147 | * Gets the achiever's progress data for this achievement, or creates a new one if not existant 148 | * @param \Illuminate\Database\Eloquent\Model $achiever 149 | * 150 | * @return AchievementProgress 151 | */ 152 | public function getOrCreateProgressForAchiever($achiever) 153 | { 154 | $className = $this->getAchieverClassName($achiever); 155 | 156 | $achievementId = $this->getModel()->id; 157 | $progress = AchievementProgress::where('achiever_type', $className) 158 | ->where('achievement_id', $achievementId) 159 | ->where('achiever_id', $achiever->id) 160 | ->first(); 161 | 162 | if (is_null($progress)) { 163 | $progress = new AchievementProgress(); 164 | $progress->details()->associate($this->getModel()); 165 | $progress->achiever()->associate($achiever); 166 | 167 | $progress->save(); 168 | } 169 | 170 | return $progress; 171 | } 172 | 173 | /** 174 | * Gets model morph name 175 | * 176 | * @param \Illuminate\Database\Eloquent\Model $achiever 177 | * @return string 178 | */ 179 | protected function getAchieverClassName($achiever) 180 | { 181 | if ($achiever instanceof \Illuminate\Database\Eloquent\Model) { 182 | return $achiever->getMorphClass(); 183 | } 184 | 185 | return get_class($achiever); 186 | } 187 | 188 | /** 189 | * Will be called when the achievement is unlocked. 190 | * 191 | * @param $progress 192 | */ 193 | public function whenUnlocked($progress) 194 | { 195 | } 196 | 197 | /** 198 | * Will be called when progress is made on the achievement. 199 | * 200 | * @param $progress 201 | */ 202 | public function whenProgress($progress) 203 | { 204 | } 205 | 206 | /** 207 | * Triggers the AchievementUnlocked Event. 208 | * 209 | * @param $progress 210 | */ 211 | public function triggerUnlocked($progress) 212 | { 213 | event(new UnlockedEvent($progress)); 214 | $this->whenUnlocked($progress); 215 | } 216 | 217 | /** 218 | * Triggers the AchievementProgress Event. 219 | * 220 | * @param $progress 221 | */ 222 | public function triggerProgress($progress) 223 | { 224 | event(new ProgressEvent($progress)); 225 | $this->whenProgress($progress); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/AchievementChain.php: -------------------------------------------------------------------------------- 1 | chain() as $instance) { 29 | /** @var Achievement $instance */ 30 | /** @var Achiever $achiever */ 31 | if($achiever->hasUnlocked($instance)){ 32 | $latestUnlocked = $achiever->achievementStatus($instance); 33 | } else { 34 | return $latestUnlocked; 35 | } 36 | } 37 | return $latestUnlocked; 38 | } 39 | 40 | public function addProgressToAchiever($achiever, $points) 41 | { 42 | foreach ($this->chain() as $instance) { 43 | /** @var Achievement $instance */ 44 | $instance->addProgressToAchiever($achiever, $points); 45 | } 46 | } 47 | 48 | public function setProgressToAchiever($achiever, $points) 49 | { 50 | foreach ($this->chain() as $instance) { 51 | /** @var Achievement $instance */ 52 | $instance->setProgressToAchiever($achiever, $points); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/AchievementsServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadMigrationsFrom(__DIR__ . '/Migrations'); 19 | if ($this->app->runningInConsole()) { 20 | $this->commands([AchievementMakeCommand::class, AchievementChainMakeCommand::class]); 21 | } 22 | $this->app['Gstt\Achievements\Achievement'] = function ($app) { 23 | return $app['gstt.achievements.achievement']; 24 | }; 25 | $this->publishes([ 26 | __DIR__ . '/config/achievements.php' => config_path('achievements.php'), 27 | ], 'config'); 28 | $this->publishes([ 29 | __DIR__.'/Migrations/0000_00_00_000000_create_achievements_tables.php' => database_path('migrations') 30 | ], 'migrations'); 31 | $this->mergeConfigFrom(__DIR__ . '/config/achievements.php', 'achievements'); 32 | } 33 | 34 | /** 35 | * Register the application services. 36 | * 37 | * @return void 38 | */ 39 | public function register() 40 | { 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Achiever.php: -------------------------------------------------------------------------------- 1 | 13 | * @license MIT License 14 | * @link https://github.com/gstt/laravel-achievements 15 | */ 16 | class AchievementChainMakeCommand extends GeneratorCommand 17 | { 18 | /** 19 | * The console command name. 20 | * 21 | * @var string 22 | */ 23 | protected $name = 'make:achievement_chain'; 24 | 25 | /** 26 | * The console command description. 27 | * 28 | * @var string 29 | */ 30 | protected $description = 'Create a new achievement chain class'; 31 | 32 | /** 33 | * The type of class being generated. 34 | * 35 | * @var string 36 | */ 37 | protected $type = 'AchievementChain'; 38 | 39 | /** 40 | * Get the stub file for the generator. 41 | * 42 | * @return string 43 | */ 44 | protected function getStub() 45 | { 46 | return __DIR__.'/stubs/achievement_chain_class.stub'; 47 | } 48 | 49 | /** 50 | * Get the default namespace for the class. 51 | * 52 | * @param string $rootNamespace The root namespace 53 | * 54 | * @return string 55 | */ 56 | protected function getDefaultNamespace($rootNamespace) 57 | { 58 | return $rootNamespace.'\Achievements'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Console/AchievementMakeCommand.php: -------------------------------------------------------------------------------- 1 | 13 | * @license MIT License 14 | * @link https://github.com/gstt/laravel-achievements 15 | */ 16 | class AchievementMakeCommand extends GeneratorCommand 17 | { 18 | /** 19 | * The console command name. 20 | * 21 | * @var string 22 | */ 23 | protected $name = 'make:achievement'; 24 | 25 | /** 26 | * The console command description. 27 | * 28 | * @var string 29 | */ 30 | protected $description = 'Create a new achievement class'; 31 | 32 | /** 33 | * The type of class being generated. 34 | * 35 | * @var string 36 | */ 37 | protected $type = 'Achievement'; 38 | 39 | /** 40 | * Get the stub file for the generator. 41 | * 42 | * @return string 43 | */ 44 | protected function getStub() 45 | { 46 | return __DIR__.'/stubs/achievement_class.stub'; 47 | } 48 | 49 | /** 50 | * Get the default namespace for the class. 51 | * 52 | * @param string $rootNamespace The root namespace 53 | * 54 | * @return string 55 | */ 56 | protected function getDefaultNamespace($rootNamespace) 57 | { 58 | return $rootNamespace.'\Achievements'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Console/stubs/achievement_chain_class.stub: -------------------------------------------------------------------------------- 1 | syncAchievements(); 27 | } 28 | return $this->morphMany(AchievementProgress::class, 'achiever') 29 | ->orderBy('updated_at', 'desc'); 30 | } 31 | 32 | /** 33 | * Retrieves the status for the specified achievement 34 | * @param Achievement $achievement 35 | * @return AchievementProgress 36 | */ 37 | public function achievementStatus(Achievement $achievement) 38 | { 39 | return $this->achievements()->where('achievement_id', $achievement->getModel()->id)->first(); 40 | } 41 | 42 | /** 43 | * Return true if the user has unlocked this achievement, false otherwise. 44 | * @param Achievement $achievement 45 | * @return bool 46 | */ 47 | public function hasUnlocked(Achievement $achievement) 48 | { 49 | $status = $this->achievementStatus($achievement); 50 | if (is_null($status) || is_null($status->unlocked_at)) { 51 | return false; 52 | } 53 | return true; 54 | } 55 | 56 | /** 57 | * Get the entity's achievements in progress. 58 | * 59 | * @return Collection 60 | */ 61 | public function inProgressAchievements() 62 | { 63 | return $this->achievements()->whereNull('unlocked_at')->where('points', '>', 0)->get(); 64 | } 65 | 66 | /** 67 | * Get the entity's unlocked achievements. 68 | * 69 | * @return Collection 70 | */ 71 | public function unlockedAchievements() 72 | { 73 | return $this->achievements()->whereNotNull('unlocked_at')->get(); 74 | } 75 | 76 | /** 77 | * Get the entity's locked achievements. 78 | */ 79 | public function lockedAchievements() 80 | { 81 | if (config('achievements.locked_sync')) { 82 | // Relationships should be synced. Just return relationship data. 83 | return $this->achievements()->whereNull('unlocked_at')->get(); 84 | } else { 85 | // Query all unsynced 86 | $unsynced = AchievementDetails::getUnsyncedByAchiever($this)->get(); 87 | $self = $this; 88 | $unsynced = $unsynced->map(function ($el) use ($self) { 89 | $progress = new AchievementProgress(); 90 | $progress->details()->associate($el); 91 | $progress->achiever()->associate($this); 92 | $progress->points = 0; 93 | $progress->created_at = null; 94 | $progress->updated_at = null; 95 | return $progress; 96 | }); 97 | 98 | // Merge with progressed, but not yet unlocked 99 | $lockedProgressed = $this->achievements()->whereNull('unlocked_at')->get(); 100 | $locked = $lockedProgressed->merge($unsynced); 101 | 102 | return $locked; 103 | } 104 | } 105 | 106 | /** 107 | * Syncs achievement data. 108 | */ 109 | public function syncAchievements() 110 | { 111 | /** @var Collection $locked */ 112 | $locked = AchievementDetails::getUnsyncedByAchiever($this); 113 | $self = $this; 114 | $locked->each(function ($el) use ($self) { 115 | $progress = new AchievementProgress(); 116 | $progress->details()->associate($el); 117 | $progress->achiever()->associate($this); 118 | $progress->points = 0; 119 | $progress->save(); 120 | }); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Event/Progress.php: -------------------------------------------------------------------------------- 1 | progress = $progress; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Event/Unlocked.php: -------------------------------------------------------------------------------- 1 | progress = $progress; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Migrations/0000_00_00_000000_create_achievements_tables.php: -------------------------------------------------------------------------------- 1 | achievement_details = Config::get('achievements.table_names.details'); 17 | $this->achievement_progress = Config::get('achievements.table_names.progress'); 18 | } 19 | 20 | /** 21 | * Run the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function up() 26 | { 27 | Schema::create($this->achievement_details, function (Blueprint $table) { 28 | $table->increments('id'); 29 | $table->string('name'); 30 | $table->string('description'); 31 | $table->unsignedInteger('points')->default(1); 32 | $table->boolean('secret')->default(false); 33 | $table->string('class_name'); 34 | $table->timestamps(); 35 | }); 36 | Schema::create($this->achievement_progress, function (Blueprint $table) { 37 | $table->uuid('id')->primary(); 38 | $table->unsignedInteger('achievement_id'); 39 | $table->morphs('achiever'); 40 | $table->unsignedInteger('points')->default(0); 41 | $table->timestamp('unlocked_at')->nullable()->default(null); 42 | $table->timestamps(); 43 | 44 | $table->foreign('achievement_id')->references('id')->on('achievement_details'); 45 | }); 46 | } 47 | 48 | /** 49 | * Reverse the migrations. 50 | * 51 | * @return void 52 | */ 53 | public function down() 54 | { 55 | Schema::dropIfExists('achievement_progress'); 56 | Schema::dropIfExists('achievement_details'); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Model/AchievementDetails.php: -------------------------------------------------------------------------------- 1 | 15 | * @license MIT License 16 | * @link https://github.com/gstt/laravel-achievements 17 | */ 18 | class AchievementDetails extends Model 19 | { 20 | public $secret = false; 21 | protected $table = 'achievement_details'; 22 | 23 | public function __construct(array $attributes = []) 24 | { 25 | $this->table = Config::get('achievements.table_names.details'); 26 | parent::__construct($attributes); 27 | } 28 | 29 | /** 30 | * Return all users that have made progress on this achievement. 31 | * 32 | * @return \Illuminate\Database\Eloquent\Relations\HasMany 33 | */ 34 | public function progress() 35 | { 36 | return $this->hasMany('Gstt\Achievements\Model\AchievementProgress', 'achievement_id'); 37 | } 38 | 39 | /** 40 | * Return the progress data for achievers that have unlocked this achievement. 41 | * 42 | * @return Collection 43 | */ 44 | public function unlocks() 45 | { 46 | return $this->progress()->whereNotNull('unlocked_at')->get(); 47 | } 48 | 49 | /** 50 | * Returns the class that defined this achievement. 51 | */ 52 | public function getClass() 53 | { 54 | return new $this->class_name(); 55 | } 56 | 57 | /** 58 | * Gets all AchievementDetails that have no correspondence on the Progress table. 59 | * 60 | * @param \Illuminate\Database\Eloquent\Model $achiever 61 | */ 62 | public static function getUnsyncedByAchiever($achiever) 63 | { 64 | $className = (new static)->getAchieverClassName($achiever); 65 | 66 | $achievements = AchievementProgress::where('achiever_type', $className) 67 | ->where('achiever_id', $achiever->id)->get(); 68 | $synced_ids = $achievements->map(function ($el) { 69 | return $el->achievement_id; 70 | })->toArray(); 71 | 72 | return self::whereNotIn('id', $synced_ids); 73 | } 74 | 75 | /** 76 | * Gets model morph name 77 | * 78 | * @param \Illuminate\Database\Eloquent\Model $achiever 79 | * @return string 80 | */ 81 | protected function getAchieverClassName($achiever) 82 | { 83 | if ($achiever instanceof \Illuminate\Database\Eloquent\Model) { 84 | return $achiever->getMorphClass(); 85 | } 86 | 87 | return get_class($achiever); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Model/AchievementProgress.php: -------------------------------------------------------------------------------- 1 | 16 | * @license MIT License 17 | * @link https://github.com/gstt/laravel-achievements 18 | */ 19 | class AchievementProgress extends Model 20 | { 21 | /** 22 | * Indicates if the IDs are auto-incrementing. 23 | * 24 | * @var bool 25 | */ 26 | public $incrementing = false; 27 | 28 | /** 29 | * The table associated with the model. 30 | * 31 | * @var string 32 | */ 33 | protected $table = 'achievement_progress'; 34 | 35 | public function __construct(array $attributes = []) 36 | { 37 | $this->table = Config::get('achievements.table_names.progress'); 38 | parent::__construct($attributes); 39 | } 40 | 41 | /** 42 | * The guarded attributes on the model. 43 | * 44 | * @var array 45 | */ 46 | protected $guarded = []; 47 | 48 | /** 49 | * The attributes that should be cast to native types. 50 | * 51 | * @var array 52 | */ 53 | protected $casts = [ 54 | 'unlocked_at' => 'datetime', 55 | ]; 56 | 57 | /** 58 | * Get the notifiable entity that the achievement progress belongs to. 59 | * 60 | * @return \Illuminate\Database\Eloquent\Relations\MorphTo 61 | */ 62 | public function achiever() 63 | { 64 | return $this->morphTo(); 65 | } 66 | 67 | /** 68 | * Get the achievement details. 69 | */ 70 | public function details() 71 | { 72 | return $this->belongsto('Gstt\Achievements\Model\AchievementDetails', 'achievement_id'); 73 | } 74 | 75 | /** 76 | * Checks if the achievement has been unlocked. 77 | * 78 | * @return bool 79 | */ 80 | public function isUnlocked() 81 | { 82 | if (!is_null($this->unlockedAt)) { 83 | return true; 84 | } 85 | if ($this->points >= $this->details->points) { 86 | return true; 87 | } 88 | return false; 89 | } 90 | 91 | /** 92 | * Checks if the achievement is locked. 93 | * 94 | * @return bool 95 | */ 96 | public function isLocked() 97 | { 98 | return !$this->isUnlocked(); 99 | } 100 | 101 | /** 102 | * Overloads save method. 103 | * 104 | * @param array $options 105 | * @return bool 106 | */ 107 | public function save(array $options = []) 108 | { 109 | if (is_null($this->id)) { 110 | $this->id = Uuid::uuid4()->toString(); 111 | } 112 | $recently_unlocked = false; 113 | if (is_null($this->unlockedAt) && $this->isUnlocked()) { 114 | $recently_unlocked = true; 115 | $this->points = $this->details->points; 116 | $this->unlocked_at = Carbon::now(); 117 | } 118 | 119 | $result = parent::save($options); 120 | 121 | // Gets the achievement class for this progress 122 | $class = $this->details->getClass(); 123 | 124 | if ($recently_unlocked) { 125 | // Runs the callback set to run when the achievement is unlocked. 126 | $class->triggerUnlocked($this); 127 | } elseif ($this->points >= 0) { 128 | // Runs the callback set to run when progress has been made on the achievement. 129 | $class->triggerProgress($this); 130 | } 131 | 132 | return $result; 133 | } 134 | 135 | /** 136 | * Maps to Gstt\Achievements\Achievement::$name 137 | * @return string 138 | */ 139 | public function getNameAttribute() 140 | { 141 | return $this->details->name; 142 | } 143 | 144 | /** 145 | * Maps to Gstt\Achievements\Achievement::$description 146 | * @return string 147 | */ 148 | public function getDescriptionAttribute() 149 | { 150 | return $this->details->description; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/RoutesAchievements.php: -------------------------------------------------------------------------------- 1 | addProgressToAchiever($this, $points); 21 | } 22 | 23 | /** 24 | * Removes a specified amount of points from the achievement. 25 | * 26 | * @param CanAchieve $instance An instance of an achievement 27 | * @param mixed $points The amount of points to remove from the achievement's progress 28 | */ 29 | public function removeProgress(CanAchieve $instance, $points = 1) 30 | { 31 | $this->addProgress($instance, (-1 * $points)); 32 | } 33 | 34 | /** 35 | * Sets the current progress as the specified amount of points. 36 | * 37 | * @param CanAchieve $instance An instance of an achievement 38 | * @param mixed $points The amount of points to remove from the achievement's progress 39 | */ 40 | public function setProgress(CanAchieve $instance, $points) 41 | { 42 | $instance->setProgressToAchiever($this, $points); 43 | } 44 | 45 | /** 46 | * Resets the achievement's progress, setting the points to 0. 47 | * 48 | * @param mixed $instance An instance of an achievement 49 | * 50 | * @return void 51 | */ 52 | public function resetProgress($instance) 53 | { 54 | $this->setProgress($instance, 0); 55 | } 56 | 57 | 58 | /** 59 | * Unlocks an achievement 60 | * 61 | * @param mixed $instance An instance of an achievement 62 | * 63 | * @return void 64 | */ 65 | public function unlock($instance) 66 | { 67 | $this->setProgress($instance, $instance->points); 68 | } 69 | 70 | /** 71 | * Gets the highest achievement unlocked on a specific achievement chain. 72 | * @param AchievementChain $chain 73 | * @return null|AchievementProgress 74 | */ 75 | public function highestOnAchievementChain(AchievementChain $chain) 76 | { 77 | return $chain->highestOnChain($this); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/config/achievements.php: -------------------------------------------------------------------------------- 1 | name, description, amount of points to unlock. 14 | | * achievement_progress stores details related to a specific unlock or progress. 15 | -> it contains ids for the achiever and achievement, as well as the amount 16 | of points obtained and when the achievement was unlocked. 17 | | 18 | | This setting allows changing the default names of these tables. 19 | | Please note that changing this before migrating will also change preemptively 20 | | the name of the created tables on the database. 21 | */ 22 | 'table_names' => [ 23 | 'details' => 'achievement_details', 24 | 'progress' => 'achievement_progress' 25 | ], 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Locked achievement sync 30 | |-------------------------------------------------------------------------- 31 | | 32 | | Controls the behavior of how locked achievements will be handled. 33 | | 34 | | Achievements are only stored on the achievement_progress table whenever 35 | | they are made progress or unlocked. Therefore, by default there is no 36 | | "locked achievement" storage. 37 | | 38 | | When set to FALSE, this will not change how the relationship works. 39 | | achievements() on the Achiever trait WILL NOT RETURN LOCKED ACHIEVEMENTS, 40 | | only returning records on the achievement_progress table. The locked() 41 | | method will act as a simple query fetching all records that exist in 42 | | achievement_details and do not have equivalent records on 43 | | achievement_progress. 44 | | 45 | | When set to TRUE, any calls to the achievements() relationship will first 46 | | fetch locked achievements and then add them to the achievement_progress 47 | | table with progress 0. Therefore, the achievements() relationship WILL 48 | | RETURN LOCKED ACHIEVEMENTS, and the locked() method will act as a derived 49 | | query from achievements(). 50 | | 51 | */ 52 | 'locked_sync' => true, 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Automatic achievement sync 57 | |-------------------------------------------------------------------------- 58 | | 59 | | Automatically syncs achievement data from source code to database tables. 60 | | 61 | | When set to true, all calls to Achievement classes will attempt to sync 62 | | data from the source code to the AchievementDetails tables. 63 | | This will keep your database in sync, but may also increase the amount 64 | | of database calls. 65 | */ 66 | 'auto_sync' => false 67 | ]; 68 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gstt/laravel-achievements/1003ea30ef936b95c6e4b602190affd3b4160b7f/tests/.gitkeep -------------------------------------------------------------------------------- /tests/AchievementChains/PostChain.php: -------------------------------------------------------------------------------- 1 | users[] = User::find(1); 23 | $this->users[] = User::find(2); 24 | $this->users[] = User::find(3); 25 | $this->users[] = User::find(4); 26 | $this->users[] = User::find(5); 27 | 28 | $this->postChain = new PostChain(); 29 | $this->firstPost = new FirstPost(); 30 | $this->tenPosts = new TenPosts(); 31 | $this->fiftyPosts = new FiftyPosts(); 32 | } 33 | 34 | /** 35 | * Test adding/removing/progressing on achievements. 36 | */ 37 | public function testProgress() 38 | { 39 | $this->users[1]->addProgress($this->postChain, 9); 40 | $this->users[3]->addProgress($this->postChain, 49); 41 | $this->users[4]->addProgress($this->postChain, 51); 42 | 43 | // At the end of this setup: 44 | // $this->users[1] should have unlocked FirstPost 45 | // $this->users[3] should have unlocked FirstPost and TenPosts 46 | // $this->users[5] should have unlocked FirstPost, TenPosts and FiftyPosts 47 | // No user should have unlocked any other achievement other than that. 48 | 49 | $this->assertFalse($this->users[0]->hasUnlocked($this->firstPost)); 50 | $this->assertTrue($this->users[1]->hasUnlocked($this->firstPost)); 51 | $this->assertFalse($this->users[2]->hasUnlocked($this->firstPost)); 52 | $this->assertTrue($this->users[3]->hasUnlocked($this->firstPost)); 53 | $this->assertTrue($this->users[4]->hasUnlocked($this->firstPost)); 54 | 55 | $this->assertFalse($this->users[0]->hasUnlocked($this->tenPosts)); 56 | $this->assertFalse($this->users[1]->hasUnlocked($this->tenPosts)); 57 | $this->assertFalse($this->users[2]->hasUnlocked($this->tenPosts)); 58 | $this->assertTrue($this->users[3]->hasUnlocked($this->tenPosts)); 59 | $this->assertTrue($this->users[4]->hasUnlocked($this->tenPosts)); 60 | 61 | $this->assertFalse($this->users[0]->hasUnlocked($this->fiftyPosts)); 62 | $this->assertFalse($this->users[1]->hasUnlocked($this->fiftyPosts)); 63 | $this->assertFalse($this->users[2]->hasUnlocked($this->fiftyPosts)); 64 | $this->assertFalse($this->users[3]->hasUnlocked($this->fiftyPosts)); 65 | $this->assertTrue($this->users[4]->hasUnlocked($this->fiftyPosts)); 66 | 67 | // Right now, we add progress to users[1] and users[3]. They should unlock Ten and FiftyPosts respectively. 68 | // All other user achievement should remain unchanged. 69 | 70 | $this->users[1]->addProgress($this->postChain); 71 | $this->users[3]->addProgress($this->postChain); 72 | 73 | $this->assertFalse($this->users[0]->hasUnlocked($this->firstPost)); 74 | $this->assertTrue($this->users[1]->hasUnlocked($this->firstPost)); 75 | $this->assertFalse($this->users[2]->hasUnlocked($this->firstPost)); 76 | $this->assertTrue($this->users[3]->hasUnlocked($this->firstPost)); 77 | $this->assertTrue($this->users[4]->hasUnlocked($this->firstPost)); 78 | 79 | $this->assertFalse($this->users[0]->hasUnlocked($this->tenPosts)); 80 | $this->assertTrue($this->users[1]->hasUnlocked($this->tenPosts)); 81 | $this->assertFalse($this->users[2]->hasUnlocked($this->tenPosts)); 82 | $this->assertTrue($this->users[3]->hasUnlocked($this->tenPosts)); 83 | $this->assertTrue($this->users[4]->hasUnlocked($this->tenPosts)); 84 | 85 | $this->assertFalse($this->users[0]->hasUnlocked($this->fiftyPosts)); 86 | $this->assertFalse($this->users[1]->hasUnlocked($this->fiftyPosts)); 87 | $this->assertFalse($this->users[2]->hasUnlocked($this->fiftyPosts)); 88 | $this->assertTrue($this->users[3]->hasUnlocked($this->fiftyPosts)); 89 | $this->assertTrue($this->users[4]->hasUnlocked($this->fiftyPosts)); 90 | 91 | // Get the highest achievement on chain for each user. 92 | 93 | $this->assertEquals(null, $this->users[0]->highestOnAchievementChain($this->postChain)); 94 | $this->assertEquals($this->tenPosts->name, $this->users[1]->highestOnAchievementChain($this->postChain)->details->name); 95 | $this->assertEquals(null, $this->users[2]->highestOnAchievementChain($this->postChain)); 96 | $this->assertEquals($this->fiftyPosts->name, $this->users[3]->highestOnAchievementChain($this->postChain)->details->name); 97 | $this->assertEquals($this->fiftyPosts->name, $this->users[4]->highestOnAchievementChain($this->postChain)->details->name); 98 | 99 | // Sets user[0] points to 15. 100 | // Sets user[1] points to 5. 101 | // Sets user[4] points to 1. 102 | // Redo assertions. 103 | 104 | $this->users[0]->setProgress($this->postChain, 15); 105 | $this->users[1]->setProgress($this->postChain, 5); 106 | $this->users[4]->setProgress($this->postChain, 1); 107 | 108 | $this->assertTrue($this->users[0]->hasUnlocked($this->firstPost)); 109 | $this->assertTrue($this->users[1]->hasUnlocked($this->firstPost)); 110 | $this->assertFalse($this->users[2]->hasUnlocked($this->firstPost)); 111 | $this->assertTrue($this->users[3]->hasUnlocked($this->firstPost)); 112 | $this->assertTrue($this->users[4]->hasUnlocked($this->firstPost)); 113 | 114 | $this->assertTrue($this->users[0]->hasUnlocked($this->tenPosts)); 115 | $this->assertTrue($this->users[1]->hasUnlocked($this->tenPosts)); 116 | $this->assertFalse($this->users[2]->hasUnlocked($this->tenPosts)); 117 | $this->assertTrue($this->users[3]->hasUnlocked($this->tenPosts)); 118 | $this->assertTrue($this->users[4]->hasUnlocked($this->tenPosts)); 119 | 120 | $this->assertFalse($this->users[0]->hasUnlocked($this->fiftyPosts)); 121 | $this->assertFalse($this->users[1]->hasUnlocked($this->fiftyPosts)); 122 | $this->assertFalse($this->users[2]->hasUnlocked($this->fiftyPosts)); 123 | $this->assertTrue($this->users[3]->hasUnlocked($this->fiftyPosts)); 124 | $this->assertTrue($this->users[4]->hasUnlocked($this->fiftyPosts)); 125 | } 126 | } -------------------------------------------------------------------------------- /tests/DBTestCase.php: -------------------------------------------------------------------------------- 1 | register('Gstt\Achievements\AchievementsServiceProvider'); 20 | $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); 21 | return $app; 22 | } 23 | 24 | /** 25 | * Setup the test environment. 26 | * 27 | * @return void 28 | */ 29 | protected function setUp(): void 30 | { 31 | parent::setUp(); 32 | 33 | $this->app['config']->set('database.default', 'sqlite'); 34 | $this->app['config']->set('database.connections.sqlite.database', ':memory:'); 35 | 36 | Artisan::call('migrate'); 37 | $this->seedUsers(); 38 | } 39 | 40 | public function seedUsers() 41 | { 42 | User::create(['name' => 'Gamer0', 'email' => 'gamer0@email.com', 'password' => '111111']); 43 | User::create(['name' => 'Gamer1', 'email' => 'gamer1@email.com', 'password' => '111111']); 44 | User::create(['name' => 'Gamer2', 'email' => 'gamer2@email.com', 'password' => '111111']); 45 | User::create(['name' => 'Gamer3', 'email' => 'gamer3@email.com', 'password' => '111111']); 46 | User::create(['name' => 'Gamer4', 'email' => 'gamer4@email.com', 'password' => '111111']); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Model/User.php: -------------------------------------------------------------------------------- 1 |11 | 12 | 16 |13 | 15 |./tests 14 |17 | 22 |18 | 19 | 20 | 21 |