├── .gitattributes
├── .gitignore
├── CONTRIBUTING.md
├── app
├── Impl
│ ├── Exception
│ │ ├── ExceptionServiceProvider.php
│ │ ├── HandlerInterface.php
│ │ ├── ImplException.php
│ │ └── NotifyHandler.php
│ ├── Pagination
│ │ └── GumbyPresenter.php
│ ├── Repo
│ │ ├── Article
│ │ │ ├── AbstractArticleDecorator.php
│ │ │ ├── ArticleInterface.php
│ │ │ ├── CacheDecorator.php
│ │ │ └── EloquentArticle.php
│ │ ├── RepoAbstract.php
│ │ ├── RepoServiceProvider.php
│ │ ├── Status
│ │ │ ├── EloquentStatus.php
│ │ │ └── StatusInterface.php
│ │ └── Tag
│ │ │ ├── EloquentTag.php
│ │ │ └── TagInterface.php
│ └── Service
│ │ ├── Cache
│ │ ├── CacheInterface.php
│ │ └── LaravelCache.php
│ │ ├── Form
│ │ ├── Article
│ │ │ ├── ArticleForm.php
│ │ │ └── ArticleFormLaravelValidator.php
│ │ └── FormServiceProvider.php
│ │ ├── Notification
│ │ ├── NotificationServiceProvider.php
│ │ ├── NotifierInterface.php
│ │ └── SmsNotifier.php
│ │ └── Validation
│ │ ├── AbstractLaravelValidator.php
│ │ └── ValidableInterface.php
├── commands
│ └── .gitkeep
├── config
│ ├── app.php
│ ├── auth.php
│ ├── cache.php
│ ├── compile.php
│ ├── database.php
│ ├── mail.php
│ ├── packages
│ │ └── .gitkeep
│ ├── queue.php
│ ├── session.php
│ ├── testing
│ │ ├── cache.php
│ │ └── session.php
│ ├── twilio.php
│ ├── view.php
│ └── workbench.php
├── controllers
│ ├── .gitkeep
│ ├── BaseController.php
│ ├── ContentController.php
│ ├── HomeController.php
│ └── admin
│ │ └── ArticleController.php
├── database
│ ├── migrations
│ │ ├── .gitkeep
│ │ ├── 2013_08_16_013614_create_articles_table.php
│ │ ├── 2013_08_16_013714_create_statuses_table.php
│ │ ├── 2013_08_16_013804_create_tags_table.php
│ │ ├── 2013_08_16_013911_create_articles_tags_table.php
│ │ └── 2013_08_18_204038_create_users_table.php
│ ├── production.sqlite
│ └── seeds
│ │ ├── .gitkeep
│ │ ├── ArticleTableSeeder.php
│ │ ├── DatabaseSeeder.php
│ │ ├── StatusesTableSeeder.php
│ │ └── UserTableSeeder.php
├── filters.php
├── lang
│ └── en
│ │ ├── pagination.php
│ │ ├── reminders.php
│ │ └── validation.php
├── models
│ ├── Article.php
│ ├── Status.php
│ ├── Tag.php
│ └── User.php
├── routes.php
├── start
│ ├── artisan.php
│ ├── global.php
│ └── local.php
├── storage
│ ├── .gitignore
│ ├── cache
│ │ └── .gitignore
│ ├── logs
│ │ └── .gitignore
│ ├── meta
│ │ └── .gitignore
│ ├── sessions
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
├── tests
│ ├── ExampleTest.php
│ └── TestCase.php
└── views
│ ├── admin
│ ├── article_create.blade.php
│ ├── article_edit.blade.php
│ └── article_list.blade.php
│ ├── article.blade.php
│ ├── emails
│ └── auth
│ │ └── reminder.blade.php
│ ├── hello.php
│ ├── home.blade.php
│ ├── layout.blade.php
│ └── pagination.php
├── artisan
├── bootstrap
├── autoload.php
├── paths.php
└── start.php
├── composer.json
├── manuscript
├── 000_thanks.txt
├── 001_who_is_this_for.txt
├── 002-5_solid.txt
├── 002_a_note_on_opinions.txt
├── 003_the_container.txt
├── 004_dependency_injection.txt
├── 005_the_sample_application.txt
├── 006_installation.txt
├── 007_application_setup.txt
├── 008_repository_pattern.txt
├── 009_repository_pattern_cacheing.txt
├── 010_validation.txt
├── 011_form_processing.txt
├── 012_error_handling.txt
├── 013_notifications.txt
├── 014_conclusion.txt
├── Book.txt
├── Preview.txt
├── Sample.txt
├── images
│ └── title_page.png
└── xxx_addendum.txt
├── phpunit.xml
├── public
├── .htaccess
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── css
│ ├── gumby.css
│ └── style.css
├── facebook.jpg
├── favicon.ico
├── favicon.png
├── fonts
│ └── icons
│ │ ├── entypo.eot
│ │ ├── entypo.ttf
│ │ └── entypo.woff
├── humans.txt
├── img
│ ├── gumby_mainlogo.png
│ ├── gumby_mainlogo@2x.png
│ └── img_silence_demo.jpg
├── index.php
├── js
│ ├── libs
│ │ ├── gumby.init.js
│ │ ├── gumby.js
│ │ ├── gumby.min.js
│ │ ├── jquery-1.10.1.min.js
│ │ ├── jquery-2.0.2.min.js
│ │ ├── jquery.mobile.custom.min.js
│ │ ├── modernizr-2.6.2.min.js
│ │ └── ui
│ │ │ ├── gumby.checkbox.js
│ │ │ ├── gumby.fittext.js
│ │ │ ├── gumby.fixed.js
│ │ │ ├── gumby.navbar.js
│ │ │ ├── gumby.radiobtn.js
│ │ │ ├── gumby.retina.js
│ │ │ ├── gumby.skiplink.js
│ │ │ ├── gumby.tabs.js
│ │ │ ├── gumby.toggleswitch.js
│ │ │ └── jquery.validation.js
│ ├── main.js
│ └── plugins.js
├── packages
│ └── .gitkeep
├── robots.txt
└── sass
│ ├── _base.scss
│ ├── _custom.scss
│ ├── _fonts.scss
│ ├── _grid.scss
│ ├── _typography.scss
│ ├── extensions
│ ├── modular-scale
│ │ ├── lib
│ │ │ └── modular-scale.rb
│ │ └── stylesheets
│ │ │ └── _modular-scale.scss
│ └── sassy-math
│ │ ├── lib
│ │ └── sassy-math.rb
│ │ └── stylesheets
│ │ └── _math.scss
│ ├── functions
│ ├── _all.scss
│ ├── _breakpoints.scss
│ ├── _buttons.scss
│ ├── _clearfix.scss
│ ├── _forms.scss
│ ├── _grid-calc.scss
│ ├── _height-calc.scss
│ ├── _line-and-height.scss
│ ├── _responsivity.scss
│ ├── _semantic-grid.scss
│ ├── _strip-units.scss
│ ├── _typography.scss
│ └── _visibility.scss
│ ├── gumby.scss
│ ├── ui
│ ├── _all.scss
│ ├── _buttons.scss
│ ├── _forms.scss
│ ├── _icons.scss
│ ├── _images.scss
│ ├── _labels.scss
│ ├── _navbar.scss
│ ├── _tables.scss
│ ├── _tabs.scss
│ ├── _toggles.scss
│ └── _video.scss
│ └── var
│ ├── _lists.scss
│ ├── _settings.scss
│ └── icons
│ ├── _entypo-icon-list.scss
│ └── _entypo.scss
├── readme.md
└── server.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bootstrap/compiled.php
2 | /vendor
3 | composer.phar
4 | composer.lock
5 | .DS_Store
6 | ._*
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guidelines
2 |
3 | Please submit all issues and pull requests to the [laravel/framework](http://github.com/laravel/framework) repository!
--------------------------------------------------------------------------------
/app/Impl/Exception/ExceptionServiceProvider.php:
--------------------------------------------------------------------------------
1 | app;
11 |
12 | $app['impl.exception'] = $app->share(function($app)
13 | {
14 | return new NotifyHandler( $app['impl.notifier'] );
15 | });
16 | }
17 |
18 | /**
19 | * Bootstrap the application events.
20 | *
21 | * @return void
22 | */
23 | public function boot()
24 | {
25 | $app = $this->app;
26 |
27 | $app->error(function(ImplException $e) use ($app)
28 | {
29 | $app['impl.exception']->handle($e);
30 | });
31 | }
32 | }
--------------------------------------------------------------------------------
/app/Impl/Exception/HandlerInterface.php:
--------------------------------------------------------------------------------
1 | notifier = $notifier;
12 | }
13 |
14 | /**
15 | * Handle Impl Exceptions
16 | *
17 | * @param \Impl\Exception\ImplException
18 | * @return void
19 | */
20 | public function handle(ImplException $exception)
21 | {
22 | $this->sendException($exception);
23 | }
24 |
25 | /**
26 | * Send Exception to notifier
27 | * @param \Exception $exception Send notification of exception
28 | * @return void
29 | */
30 | protected function sendException(\Exception $e)
31 | {
32 | $this->notifier->notify('Error: '.get_class($e), $e->getMessage());
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/Impl/Repo/Article/AbstractArticleDecorator.php:
--------------------------------------------------------------------------------
1 | nextArticle = $nextArticle;
10 | }
11 |
12 | /**
13 | * {@inheritdoc}
14 | */
15 | public function byId($id)
16 | {
17 | return $this->nextArticle->byId($id);
18 | }
19 |
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public function byPage($page=1, $limit=10, $all=false)
24 | {
25 | return $this->nextArticle->byPage($page, $limit, $all);
26 | }
27 |
28 | /**
29 | * {@inheritdoc}
30 | */
31 | public function bySlug($slug)
32 | {
33 | return $this->nextArticle->bySlug($slug);
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function byTag($tag, $page=1, $limit=10)
40 | {
41 | return $this->nextArticle->byTag($tag, $page, $limit);
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | public function create(array $data)
48 | {
49 | return $this->nextArticle->create($data);
50 | }
51 |
52 | /**
53 | * {@inheritdoc}
54 | */
55 | public function update(array $data)
56 | {
57 | return $this->nextArticle->update($data);
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/app/Impl/Repo/Article/ArticleInterface.php:
--------------------------------------------------------------------------------
1 | cache = $cache;
13 | }
14 |
15 | /**
16 | * Attempt to retrieve from cache
17 | * by ID
18 | * {@inheritdoc}
19 | */
20 | public function byId($id)
21 | {
22 | $key = md5('id.'.$id);
23 |
24 | if( $this->cache->has($key) )
25 | {
26 | return $this->cache->get($key);
27 | }
28 |
29 | $article = $this->nextArticle->byId($id);
30 |
31 | $this->cache->put($key, $article);
32 |
33 | return $article;
34 | }
35 |
36 | /**
37 | * Attempt to retrieve from cache
38 | * {@inheritdoc}
39 | */
40 | public function byPage($page=1, $limit=10, $all=false)
41 | {
42 | $allkey = ($all) ? '.all' : '';
43 | $key = md5('page.'.$page.'.'.$limit.$allkey);
44 |
45 | if( $this->cache->has($key) )
46 | {
47 | return $this->cache->get($key);
48 | }
49 |
50 | $paginated = $this->nextArticle->byPage($page, $limit);
51 |
52 | $this->cache->put($key, $paginated);
53 |
54 | return $paginated;
55 | }
56 |
57 | /**
58 | * Attempt to retrieve from cache
59 | * {@inheritdoc}
60 | */
61 | public function bySlug($slug)
62 | {
63 | $key = md5('slug.'.$slug);
64 |
65 | if( $this->cache->has($key) )
66 | {
67 | return $this->cache->get($key);
68 | }
69 |
70 | $article = $this->nextArticle->bySlug($slug);
71 |
72 | $this->cache->put($key, $article);
73 |
74 | return $article;
75 | }
76 |
77 | /**
78 | * Attempt to retrieve from cache
79 | * {@inheritdoc}
80 | */
81 | public function byTag($tag, $page=1, $limit=10)
82 | {
83 | $key = md5('tag.'.$tag.'.'.$page.'.'.$limit);
84 |
85 | if( $this->cache->has($key) )
86 | {
87 | return $this->cache->get($key);
88 | }
89 |
90 | $paginated = $this->nextArticle->byTag($tag, $page, $limit);
91 |
92 | $this->cache->put($key, $paginated);
93 |
94 | return $paginated;
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/app/Impl/Repo/RepoAbstract.php:
--------------------------------------------------------------------------------
1 | app;
23 |
24 | $app->bind('Impl\Repo\Article\ArticleInterface', function($app)
25 | {
26 | $article = new EloquentArticle(
27 | new Article,
28 | $app->make('Impl\Repo\Tag\TagInterface')
29 | );
30 |
31 | if( $app['config']->get('is_admin', false) == false )
32 | {
33 | $article = new CacheDecorator(
34 | $article,
35 | new LaravelCache($app['cache'], 'articles', 10)
36 | );
37 | }
38 |
39 | return $article;
40 |
41 | });
42 |
43 | $app->bind('Impl\Repo\Tag\TagInterface', function($app)
44 | {
45 | return new EloquentTag(
46 | new Tag,
47 | new LaravelCache($app['cache'], 'tags', 10)
48 | );
49 | });
50 |
51 | $app->bind('Impl\Repo\Status\StatusInterface', function($app)
52 | {
53 | return new EloquentStatus(
54 | new Status
55 | );
56 | });
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/app/Impl/Repo/Status/EloquentStatus.php:
--------------------------------------------------------------------------------
1 | status = $status;
13 | }
14 |
15 | /**
16 | * Get all Statuses
17 | * @return Array Arrayable collection
18 | */
19 | public function all()
20 | {
21 | return $this->status->all();
22 | }
23 |
24 | /**
25 | * Get specific status
26 | * @param int $id Status ID
27 | * @return object Status object
28 | */
29 | public function byId($id)
30 | {
31 | return $this->status->find($id);
32 | }
33 |
34 | /**
35 | * Get specific status
36 | * @param string $slug Status slug
37 | * @return object Status object
38 | */
39 | public function byStatus($slug)
40 | {
41 | return $this->status->where('slug', $slug);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/Impl/Repo/Status/StatusInterface.php:
--------------------------------------------------------------------------------
1 | tag = $tag;
16 | $this->cache = $cache;
17 | }
18 |
19 | /**
20 | * Find existing tags or create if they don't exist
21 | *
22 | * @param array $tags Array of strings, each representing a tag
23 | * @return array Array or Arrayable collection of Tag objects
24 | */
25 | public function findOrCreate(array $tags)
26 | {
27 | $foundTags = $this->tag->whereIn('tag', $tags)->get();
28 |
29 | $returnTags = array();
30 |
31 | if( $foundTags )
32 | {
33 | foreach( $foundTags as $tag )
34 | {
35 | $pos = array_search($tag->tag, $tags);
36 |
37 | // Add returned tags to array
38 | if( $pos !== false )
39 | {
40 | $returnTags[] = $tag;
41 | unset($tags[$pos]);
42 | }
43 | }
44 | }
45 |
46 | // Add remainings tags as new
47 | foreach( $tags as $tag )
48 | {
49 | $returnTags[] = $this->tag->create(array(
50 | 'tag' => $tag,
51 | 'slug' => $this->slug($tag),
52 | ));
53 | }
54 |
55 | return $returnTags;
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/app/Impl/Repo/Tag/TagInterface.php:
--------------------------------------------------------------------------------
1 | cache = $cache;
14 | $this->cachekey = $cachekey;
15 | $this->minutes = $minutes;
16 | }
17 |
18 | /**
19 | * Retrieve data from cache
20 | *
21 | * @param string Cache item key
22 | * @return mixed PHP data result of cache
23 | */
24 | public function get($key)
25 | {
26 | return $this->cache->section($this->cachekey)->get($key);
27 | }
28 |
29 | /**
30 | * Add data to the cache
31 | *
32 | * @param string Cache item key
33 | * @param mixed The data to store
34 | * @param integer The number of minutes to store the item
35 | * @return mixed $value variable returned for convenience
36 | */
37 | public function put($key, $value, $minutes=null)
38 | {
39 | if( is_null($minutes) )
40 | {
41 | $minutes = $this->minutes;
42 | }
43 |
44 | return $this->cache->section($this->cachekey)->put($key, $value, $minutes);
45 | }
46 |
47 | /**
48 | * Add data to the cache
49 | * taking pagination data into account
50 | *
51 | * @param integer Page of the cached items
52 | * @param integer Number of results per page
53 | * @param integer Total number of possible items
54 | * @param mixed The actual items for this page
55 | * @param string Cache item key
56 | * @param integer The number of minutes to store the item
57 | * @return mixed $items variable returned for convenience
58 | */
59 | public function putPaginated($currentPage, $perPage, $totalItems, $items, $key, $minutes=null)
60 | {
61 | $cached = new \StdClass;
62 |
63 | $cached->currentPage = $currentPage;
64 | $cached->items = $items;
65 | $cached->totalItems = $totalItems;
66 | $cached->perPage = $perPage;
67 |
68 | $this->put($key, $cached, $minutes);
69 |
70 | return $cached;
71 | }
72 |
73 | /**
74 | * Test if item exists in cache
75 | * Only returns true if exists && is not expired
76 | *
77 | * @param string Cache item key
78 | * @return bool If cache item exists
79 | */
80 | public function has($key)
81 | {
82 | return $this->cache->section($this->cachekey)->has($key);
83 | }
84 |
85 | }
--------------------------------------------------------------------------------
/app/Impl/Service/Form/Article/ArticleForm.php:
--------------------------------------------------------------------------------
1 | validator = $validator;
32 | $this->article = $article;
33 | }
34 |
35 | /**
36 | * Create an new article
37 | *
38 | * @return boolean
39 | */
40 | public function save(array $input)
41 | {
42 | if( ! $this->valid($input) )
43 | {
44 | return false;
45 | }
46 |
47 | $input['tags'] = $this->processTags($input['tags']);
48 |
49 | return $this->article->create($input);
50 | }
51 |
52 | /**
53 | * Update an existing article
54 | *
55 | * @return boolean
56 | */
57 | public function update(array $input)
58 | {
59 | if( ! $this->valid($input) )
60 | {
61 | return false;
62 | }
63 |
64 | $input['tags'] = $this->processTags($input['tags']);
65 |
66 | return $this->article->update($input);
67 | }
68 |
69 | /**
70 | * Return any validation errors
71 | *
72 | * @return array
73 | */
74 | public function errors()
75 | {
76 | return $this->validator->errors();
77 | }
78 |
79 | /**
80 | * Test if form validator passes
81 | *
82 | * @return boolean
83 | */
84 | protected function valid(array $input)
85 | {
86 | return $this->validator->with($input)->passes();
87 | }
88 |
89 | /**
90 | * Convert string of tags to
91 | * array of tags
92 | *
93 | * @param string
94 | * @return array
95 | */
96 | protected function processTags($tags)
97 | {
98 | $tags = explode(',', $tags);
99 |
100 | foreach( $tags as $key => $tag )
101 | {
102 | $tags[$key] = trim($tag);
103 | }
104 |
105 | return $tags;
106 | }
107 |
108 | }
--------------------------------------------------------------------------------
/app/Impl/Service/Form/Article/ArticleFormLaravelValidator.php:
--------------------------------------------------------------------------------
1 | 'required',
14 | 'user_id' => 'required|exists:users,id', // Assumes db connection
15 | 'status_id' => 'required|exists:statuses,id', // Assumes db connection
16 | 'excerpt' => 'required',
17 | 'content' => 'required',
18 | 'tags' => 'required',
19 | );
20 |
21 | }
--------------------------------------------------------------------------------
/app/Impl/Service/Form/FormServiceProvider.php:
--------------------------------------------------------------------------------
1 | app;
17 |
18 | $app->bind('Impl\Service\Form\Article\ArticleForm', function($app)
19 | {
20 | return new ArticleForm(
21 | new ArticleFormLaravelValidator( $app['validator'] ),
22 | $app->make('Impl\Repo\Article\ArticleInterface')
23 | );
24 | });
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/Impl/Service/Notification/NotificationServiceProvider.php:
--------------------------------------------------------------------------------
1 | app;
16 |
17 | $app['impl.notifier'] = $app->share(function() use ($app)
18 | {
19 | $config = $app['config'];
20 |
21 | $twilio = new Services_Twilio(
22 | $config->get('twilio.account_id'),
23 | $config->get('twilio.auth_token')
24 | );
25 |
26 | $notifier = new SmsNotifier( $twilio );
27 |
28 | $notifier->from( $config['twilio.from'] )
29 | ->to( $config['twilio.to'] );
30 |
31 | return $notifier;
32 | });
33 | }
34 |
35 | public function provides()
36 | {
37 | return array('impl.notifier');
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/app/Impl/Service/Notification/NotifierInterface.php:
--------------------------------------------------------------------------------
1 | twilio = $twilio;
28 | }
29 |
30 | /**
31 | * Recipients of notification
32 | * @param string $to The recipient
33 | * @return Impl\Service\Notification\SmsNotifier $this Return self for chainability
34 | */
35 | public function to($to)
36 | {
37 | $this->to = $to;
38 |
39 | return $this;
40 | }
41 |
42 | /**
43 | * Sender of notification
44 | * @param string $from The sender
45 | * @return Impl\Service\Notification\NotifierInterface $this Return self for chainability
46 | */
47 | public function from($from)
48 | {
49 | $this->from = $from;
50 |
51 | return $this;
52 | }
53 |
54 | public function notify($subject, $message)
55 | {
56 | $sms = $this->twilio
57 | ->account
58 | ->sms_messages
59 | ->create(
60 | $this->from,
61 | $this->to,
62 | $subject."\n".$message
63 | );
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/app/Impl/Service/Validation/AbstractLaravelValidator.php:
--------------------------------------------------------------------------------
1 | value array
16 | *
17 | * @var Array
18 | */
19 | protected $data = array();
20 |
21 | /**
22 | * Validation errors
23 | *
24 | * @var Array
25 | */
26 | protected $errors = array();
27 |
28 | /**
29 | * Validation rules
30 | *
31 | * @var Array
32 | */
33 | protected $rules = array();
34 |
35 | public function __construct(Factory $validator)
36 | {
37 | $this->validator = $validator;
38 | }
39 |
40 | /**
41 | * Set data to validate
42 | *
43 | * @return \Impl\Service\Validation\AbstractLaravelValidator
44 | */
45 | public function with(array $data)
46 | {
47 | $this->data = $data;
48 |
49 | return $this;
50 | }
51 |
52 | /**
53 | * Validation passes or fails
54 | *
55 | * @return Boolean
56 | */
57 | public function passes()
58 | {
59 | $validator = $this->validator->make($this->data, $this->rules);
60 |
61 | if( $validator->fails() )
62 | {
63 | $this->errors = $validator->messages();
64 | return false;
65 | }
66 |
67 | return true;
68 | }
69 |
70 | /**
71 | * Return errors, if any
72 | *
73 | * @return array
74 | */
75 | public function errors()
76 | {
77 | return $this->errors;
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/app/Impl/Service/Validation/ValidableInterface.php:
--------------------------------------------------------------------------------
1 | 'eloquent',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Authentication Model
23 | |--------------------------------------------------------------------------
24 | |
25 | | When using the "Eloquent" authentication driver, we need to know which
26 | | Eloquent model should be used to retrieve your users. Of course, it
27 | | is often just the "User" model but you may use whatever you like.
28 | |
29 | */
30 |
31 | 'model' => 'User',
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Authentication Table
36 | |--------------------------------------------------------------------------
37 | |
38 | | When using the "Database" authentication driver, we need to know which
39 | | table should be used to retrieve your users. We have chosen a basic
40 | | default value but you may easily change it to any table you like.
41 | |
42 | */
43 |
44 | 'table' => 'users',
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Password Reminder Settings
49 | |--------------------------------------------------------------------------
50 | |
51 | | Here you may set the settings for password reminders, including a view
52 | | that should be used as your password reminder e-mail. You will also
53 | | be able to set the name of the table that holds the reset tokens.
54 | |
55 | */
56 |
57 | 'reminder' => array(
58 |
59 | 'email' => 'emails.auth.reminder',
60 |
61 | 'table' => 'password_reminders',
62 |
63 | 'expire' => 60,
64 |
65 | ),
66 |
67 | );
--------------------------------------------------------------------------------
/app/config/cache.php:
--------------------------------------------------------------------------------
1 | 'memcached',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | File Cache Location
23 | |--------------------------------------------------------------------------
24 | |
25 | | When using the "file" cache driver, we need a location where the cache
26 | | files may be stored. A sensible default has been specified, but you
27 | | are free to change it to any other place on disk that you desire.
28 | |
29 | */
30 |
31 | 'path' => storage_path().'/cache',
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Database Cache Connection
36 | |--------------------------------------------------------------------------
37 | |
38 | | When using the "database" cache driver you may specify the connection
39 | | that should be used to store the cached items. When this option is
40 | | null the default database connection will be utilized for cache.
41 | |
42 | */
43 |
44 | 'connection' => null,
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Database Cache Table
49 | |--------------------------------------------------------------------------
50 | |
51 | | When using the "database" cache driver we need to know the table that
52 | | should be used to store the cached items. A default table name has
53 | | been provided but you're free to change it however you deem fit.
54 | |
55 | */
56 |
57 | 'table' => 'cache',
58 |
59 | /*
60 | |--------------------------------------------------------------------------
61 | | Memcached Servers
62 | |--------------------------------------------------------------------------
63 | |
64 | | Now you may specify an array of your Memcached servers that should be
65 | | used when utilizing the Memcached cache driver. All of the servers
66 | | should contain a value for "host", "port", and "weight" options.
67 | |
68 | */
69 |
70 | 'memcached' => array(
71 |
72 | array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100),
73 |
74 | ),
75 |
76 | /*
77 | |--------------------------------------------------------------------------
78 | | Cache Key Prefix
79 | |--------------------------------------------------------------------------
80 | |
81 | | When utilizing a RAM based store such as APC or Memcached, there might
82 | | be other applications utilizing the same cache. So, we'll specify a
83 | | value to get prefixed to all our keys so we can avoid collisions.
84 | |
85 | */
86 |
87 | 'prefix' => 'laravel',
88 |
89 | );
90 |
--------------------------------------------------------------------------------
/app/config/compile.php:
--------------------------------------------------------------------------------
1 | PDO::FETCH_CLASS,
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Default Database Connection Name
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may specify which of the database connections below you wish
24 | | to use as your default connection for all database work. Of course
25 | | you may use many connections at once using the Database library.
26 | |
27 | */
28 |
29 | 'default' => 'mysql',
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Database Connections
34 | |--------------------------------------------------------------------------
35 | |
36 | | Here are each of the database connections setup for your application.
37 | | Of course, examples of configuring each database platform that is
38 | | supported by Laravel is shown below to make development simple.
39 | |
40 | |
41 | | All database work in Laravel is done through the PHP PDO facilities
42 | | so make sure you have the driver for your particular database of
43 | | choice installed on your machine before you begin development.
44 | |
45 | */
46 |
47 | 'connections' => array(
48 |
49 | 'sqlite' => array(
50 | 'driver' => 'sqlite',
51 | 'database' => __DIR__.'/../database/production.sqlite',
52 | 'prefix' => '',
53 | ),
54 |
55 | 'mysql' => array(
56 | 'driver' => 'mysql',
57 | 'host' => 'localhost',
58 | 'database' => 'implementing',
59 | 'username' => 'root',
60 | 'password' => 'root',
61 | 'charset' => 'utf8',
62 | 'collation' => 'utf8_unicode_ci',
63 | 'prefix' => '',
64 | ),
65 |
66 | 'pgsql' => array(
67 | 'driver' => 'pgsql',
68 | 'host' => 'localhost',
69 | 'database' => 'database',
70 | 'username' => 'root',
71 | 'password' => '',
72 | 'charset' => 'utf8',
73 | 'prefix' => '',
74 | 'schema' => 'public',
75 | ),
76 |
77 | 'sqlsrv' => array(
78 | 'driver' => 'sqlsrv',
79 | 'host' => 'localhost',
80 | 'database' => 'database',
81 | 'username' => 'root',
82 | 'password' => '',
83 | 'prefix' => '',
84 | ),
85 |
86 | ),
87 |
88 | /*
89 | |--------------------------------------------------------------------------
90 | | Migration Repository Table
91 | |--------------------------------------------------------------------------
92 | |
93 | | This table keeps track of all the migrations that have already run for
94 | | your application. Using this information, we can determine which of
95 | | the migrations on disk have not actually be run in the databases.
96 | |
97 | */
98 |
99 | 'migrations' => 'migrations',
100 |
101 | /*
102 | |--------------------------------------------------------------------------
103 | | Redis Databases
104 | |--------------------------------------------------------------------------
105 | |
106 | | Redis is an open source, fast, and advanced key-value store that also
107 | | provides a richer set of commands than a typical key-value systems
108 | | such as APC or Memcached. Laravel makes it easy to dig right in.
109 | |
110 | */
111 |
112 | 'redis' => array(
113 |
114 | 'cluster' => true,
115 |
116 | 'default' => array(
117 | 'host' => '127.0.0.1',
118 | 'port' => 6379,
119 | 'database' => 0,
120 | ),
121 |
122 | ),
123 |
124 | );
125 |
--------------------------------------------------------------------------------
/app/config/mail.php:
--------------------------------------------------------------------------------
1 | 'smtp',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | SMTP Host Address
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may provide the host address of the SMTP server used by your
26 | | applications. A default option is provided that is compatible with
27 | | the Postmark mail service, which will provide reliable delivery.
28 | |
29 | */
30 |
31 | 'host' => 'smtp.mailgun.org',
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | SMTP Host Port
36 | |--------------------------------------------------------------------------
37 | |
38 | | This is the SMTP port used by your application to delivery e-mails to
39 | | users of your application. Like the host we have set this value to
40 | | stay compatible with the Postmark e-mail application by default.
41 | |
42 | */
43 |
44 | 'port' => 587,
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Global "From" Address
49 | |--------------------------------------------------------------------------
50 | |
51 | | You may wish for all e-mails sent by your application to be sent from
52 | | the same address. Here, you may specify a name and address that is
53 | | used globally for all e-mails that are sent by your application.
54 | |
55 | */
56 |
57 | 'from' => array('address' => null, 'name' => null),
58 |
59 | /*
60 | |--------------------------------------------------------------------------
61 | | E-Mail Encryption Protocol
62 | |--------------------------------------------------------------------------
63 | |
64 | | Here you may specify the encryption protocol that should be used when
65 | | the application send e-mail messages. A sensible default using the
66 | | transport layer security protocol should provide great security.
67 | |
68 | */
69 |
70 | 'encryption' => 'tls',
71 |
72 | /*
73 | |--------------------------------------------------------------------------
74 | | SMTP Server Username
75 | |--------------------------------------------------------------------------
76 | |
77 | | If your SMTP server requires a username for authentication, you should
78 | | set it here. This will get used to authenticate with your server on
79 | | connection. You may also set the "password" value below this one.
80 | |
81 | */
82 |
83 | 'username' => null,
84 |
85 | /*
86 | |--------------------------------------------------------------------------
87 | | SMTP Server Password
88 | |--------------------------------------------------------------------------
89 | |
90 | | Here you may set the password required by your SMTP server to send out
91 | | messages from your application. This will be given to the server on
92 | | connection so that the application will be able to send messages.
93 | |
94 | */
95 |
96 | 'password' => null,
97 |
98 | /*
99 | |--------------------------------------------------------------------------
100 | | Sendmail System Path
101 | |--------------------------------------------------------------------------
102 | |
103 | | When using the "sendmail" driver to send e-mails, we will need to know
104 | | the path to where Sendmail lives on this server. A default path has
105 | | been provided here, which will work well on most of your systems.
106 | |
107 | */
108 |
109 | 'sendmail' => '/usr/sbin/sendmail -bs',
110 |
111 | /*
112 | |--------------------------------------------------------------------------
113 | | Mail "Pretend"
114 | |--------------------------------------------------------------------------
115 | |
116 | | When this option is enabled, e-mail will not actually be sent over the
117 | | web and will instead be written to your application's logs files so
118 | | you may inspect the message. This is great for local development.
119 | |
120 | */
121 |
122 | 'pretend' => false,
123 |
124 | );
--------------------------------------------------------------------------------
/app/config/packages/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/app/config/packages/.gitkeep
--------------------------------------------------------------------------------
/app/config/queue.php:
--------------------------------------------------------------------------------
1 | 'sync',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Queue Connections
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may configure the connection information for each server that
26 | | is used by your application. A default configuration has been added
27 | | for each back-end shipped with Laravel. You are free to add more.
28 | |
29 | */
30 |
31 | 'connections' => array(
32 |
33 | 'sync' => array(
34 | 'driver' => 'sync',
35 | ),
36 |
37 | 'beanstalkd' => array(
38 | 'driver' => 'beanstalkd',
39 | 'host' => 'localhost',
40 | 'queue' => 'default',
41 | ),
42 |
43 | 'sqs' => array(
44 | 'driver' => 'sqs',
45 | 'key' => 'your-public-key',
46 | 'secret' => 'your-secret-key',
47 | 'queue' => 'your-queue-url',
48 | 'region' => 'us-east-1',
49 | ),
50 |
51 | 'iron' => array(
52 | 'driver' => 'iron',
53 | 'project' => 'your-project-id',
54 | 'token' => 'your-token',
55 | 'queue' => 'your-queue-name',
56 | ),
57 |
58 | ),
59 |
60 | );
61 |
--------------------------------------------------------------------------------
/app/config/session.php:
--------------------------------------------------------------------------------
1 | 'native',
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Session Lifetime
24 | |--------------------------------------------------------------------------
25 | |
26 | | Here you may specify the number of minutes that you wish the session
27 | | to be allowed to remain idle for it is expired. If you want them
28 | | to immediately expire when the browser closes, set it to zero.
29 | |
30 | */
31 |
32 | 'lifetime' => 120,
33 |
34 | /*
35 | |--------------------------------------------------------------------------
36 | | Session File Location
37 | |--------------------------------------------------------------------------
38 | |
39 | | When using the native session driver, we need a location where session
40 | | files may be stored. A default has been set for you but a different
41 | | location may be specified. This is only needed for file sessions.
42 | |
43 | */
44 |
45 | 'files' => storage_path().'/sessions',
46 |
47 | /*
48 | |--------------------------------------------------------------------------
49 | | Session Database Connection
50 | |--------------------------------------------------------------------------
51 | |
52 | | When using the "database" session driver, you may specify the database
53 | | connection that should be used to manage your sessions. This should
54 | | correspond to a connection in your "database" configuration file.
55 | |
56 | */
57 |
58 | 'connection' => null,
59 |
60 | /*
61 | |--------------------------------------------------------------------------
62 | | Session Database Table
63 | |--------------------------------------------------------------------------
64 | |
65 | | When using the "database" session driver, you may specify the table we
66 | | should use to manage the sessions. Of course, a sensible default is
67 | | provided for you; however, you are free to change this as needed.
68 | |
69 | */
70 |
71 | 'table' => 'sessions',
72 |
73 | /*
74 | |--------------------------------------------------------------------------
75 | | Session Sweeping Lottery
76 | |--------------------------------------------------------------------------
77 | |
78 | | Some session drivers must manually sweep their storage location to get
79 | | rid of old sessions from storage. Here are the chances that it will
80 | | happen on a given request. By default, the odds are 2 out of 100.
81 | |
82 | */
83 |
84 | 'lottery' => array(2, 100),
85 |
86 | /*
87 | |--------------------------------------------------------------------------
88 | | Session Cookie Name
89 | |--------------------------------------------------------------------------
90 | |
91 | | Here you may change the name of the cookie used to identify a session
92 | | instance by ID. The name specified here will get used every time a
93 | | new session cookie is created by the framework for every driver.
94 | |
95 | */
96 |
97 | 'cookie' => 'laravel_session',
98 |
99 | /*
100 | |--------------------------------------------------------------------------
101 | | Session Cookie Path
102 | |--------------------------------------------------------------------------
103 | |
104 | | The session cookie path determines the path for which the cookie will
105 | | be regarded as available. Typically, this will be the root path of
106 | | your application but you are free to change this when necessary.
107 | |
108 | */
109 |
110 | 'path' => '/',
111 |
112 | /*
113 | |--------------------------------------------------------------------------
114 | | Session Cookie Domain
115 | |--------------------------------------------------------------------------
116 | |
117 | | Here you may change the domain of the cookie used to identify a session
118 | | in your application. This will determine which domains the cookie is
119 | | available to in your application. A sensible default has been set.
120 | |
121 | */
122 |
123 | 'domain' => null,
124 |
125 | );
126 |
--------------------------------------------------------------------------------
/app/config/testing/cache.php:
--------------------------------------------------------------------------------
1 | 'array',
19 |
20 | );
--------------------------------------------------------------------------------
/app/config/testing/session.php:
--------------------------------------------------------------------------------
1 | 'array',
20 |
21 | );
--------------------------------------------------------------------------------
/app/config/twilio.php:
--------------------------------------------------------------------------------
1 | '555-1234',
6 |
7 | 'to' => '555-5678',
8 |
9 | 'account_id' => 'abc1234',
10 |
11 | 'auth_token' => '1111111',
12 |
13 | );
--------------------------------------------------------------------------------
/app/config/view.php:
--------------------------------------------------------------------------------
1 | array(__DIR__.'/../views'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Pagination View
21 | |--------------------------------------------------------------------------
22 | |
23 | | This view will be used to render the pagination link output, and can
24 | | be easily customized here to show any view you like. A clean view
25 | | compatible with Twitter's Bootstrap is given to you by default.
26 | |
27 | */
28 |
29 | 'pagination' => 'pagination',
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/app/config/workbench.php:
--------------------------------------------------------------------------------
1 | '',
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Workbench Author E-Mail Address
21 | |--------------------------------------------------------------------------
22 | |
23 | | Like the option above, your e-mail address is used when generating new
24 | | workbench packages. The e-mail is placed in your composer.json file
25 | | automatically after the package is created by the workbench tool.
26 | |
27 | */
28 |
29 | 'email' => '',
30 |
31 | );
--------------------------------------------------------------------------------
/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/app/controllers/.gitkeep
--------------------------------------------------------------------------------
/app/controllers/BaseController.php:
--------------------------------------------------------------------------------
1 | layout))
13 | {
14 | $this->layout = View::make($this->layout);
15 | }
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/app/controllers/ContentController.php:
--------------------------------------------------------------------------------
1 | article = $article;
14 | }
15 |
16 | /**
17 | * Paginated articles
18 | * GET /
19 | */
20 | public function home()
21 | {
22 | $page = Input::get('page', 1);
23 |
24 | // Candidate for config item
25 | $perPage = 3;
26 |
27 | $pagiData = $this->article->byPage($page, $perPage);
28 |
29 | $articles = Paginator::make($pagiData->items, $pagiData->totalItems, $perPage);
30 |
31 | $this->layout->content = View::make('home')->with('articles', $articles);
32 | }
33 |
34 | /**
35 | * Single article
36 | * GET /{slug}
37 | */
38 | public function article($slug)
39 | {
40 | $article = $this->article->bySlug($slug);
41 |
42 | if( ! $article )
43 | {
44 | App::abort(404);
45 | }
46 |
47 | $this->layout->content = View::make('article')->with('article', $article);
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/app/controllers/HomeController.php:
--------------------------------------------------------------------------------
1 | article = $article;
18 | $this->articleform = $articleform;
19 | $this->status = $status;
20 | }
21 |
22 | /**
23 | * List articles
24 | * GET /admin/article
25 | */
26 | public function index()
27 | {
28 | $page = Input::get('page', 1);
29 |
30 | // Candidate for config item
31 | $perPage = 3;
32 |
33 | $pagiData = $this->article->byPage($page, $perPage, true);
34 |
35 | $articles = Paginator::make($pagiData->items, $pagiData->totalItems, $perPage);
36 |
37 | $this->layout->content = View::make('admin.article_list')->with('articles', $articles);
38 | }
39 |
40 | /**
41 | * Show single article. We only want to show edit form
42 | * @param int $id Article ID
43 | * @return Redirect
44 | */
45 | public function show($id)
46 | {
47 | return Redirect::to('/admin/article/'.$id.'/edit');
48 | }
49 |
50 | /**
51 | * Create article form
52 | * GET /admin/article/create
53 | */
54 | public function create()
55 | {
56 | $statuses = $this->status->all();
57 |
58 | $this->layout->content = View::make('admin.article_create', array(
59 | 'statuses' => $statuses,
60 | 'input' => Session::getOldInput(),
61 | ));
62 | }
63 |
64 | /**
65 | * Create article form processing
66 | * POST /admin/article
67 | */
68 | public function store()
69 | {
70 | $input = array_merge(Input::all(), array('user_id' => 1));
71 |
72 | if( $this->articleform->save( $input ) )
73 | {
74 | // Success!
75 | return Redirect::to('/admin/article')
76 | ->with('status', 'success');
77 | } else {
78 |
79 | return Redirect::to('/admin/article/create')
80 | ->withInput()
81 | ->withErrors( $this->articleform->errors() )
82 | ->with('status', 'error');
83 | }
84 | }
85 |
86 | /**
87 | * Create article form
88 | * GET /admin/article/{id}/edit
89 | */
90 | public function edit($id)
91 | {
92 | $article = $this->article->byId($id);
93 | $statuses = $this->status->all();
94 |
95 | $tags = '';
96 | $article->tags->each(function($tag) use(&$tags)
97 | {
98 | $tags .= $tag->tag.', ';
99 | });
100 |
101 | $tags = substr($tags, 0, -2);
102 |
103 | $this->layout->content = View::make('admin.article_edit', array(
104 | 'article' => $article,
105 | 'tags' => $tags,
106 | 'statuses' => $statuses,
107 | 'input' => Session::getOldInput()
108 | ));
109 | }
110 |
111 | /**
112 | * Create article form
113 | * PUT /admin/article/{id}
114 | */
115 | public function update()
116 | {
117 | $input = array_merge(Input::all(), array('user_id' => 1));
118 |
119 | if( $this->articleform->update( $input ) )
120 | {
121 | // Success!
122 | return Redirect::to('admin/article')
123 | ->with('status', 'success');
124 | } else {
125 |
126 | // Need article ID
127 | return Redirect::to('admin/article/'.Input::get('id').'/edit')
128 | ->withInput()
129 | ->withErrors( $this->articleform->errors() )
130 | ->with('status', 'error');
131 | }
132 | }
133 |
134 | }
--------------------------------------------------------------------------------
/app/database/migrations/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/app/database/migrations/.gitkeep
--------------------------------------------------------------------------------
/app/database/migrations/2013_08_16_013614_create_articles_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->integer('user_id')->unsigned();
19 | $table->integer('status_id')->unsigned();
20 | $table->string('title');
21 | $table->string('slug');
22 | $table->text('excerpt');
23 | $table->text('content');
24 | $table->softDeletes();
25 | $table->timestamps();
26 | });
27 | }
28 |
29 | /**
30 | * Reverse the migrations.
31 | *
32 | * @return void
33 | */
34 | public function down()
35 | {
36 | Schema::drop('articles');
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/app/database/migrations/2013_08_16_013714_create_statuses_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->string('status');
19 | $table->string('slug');
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | *
27 | * @return void
28 | */
29 | public function down()
30 | {
31 | Schema::drop('statuses');
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/database/migrations/2013_08_16_013804_create_tags_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->string('tag');
19 | $table->string('slug');
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | *
26 | * @return void
27 | */
28 | public function down()
29 | {
30 | Schema::drop('tags');
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/database/migrations/2013_08_16_013911_create_articles_tags_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->integer('article_id')->unsigned()->index();
19 | $table->integer('tag_id')->unsigned()->index();
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | *
27 | * @return void
28 | */
29 | public function down()
30 | {
31 | Schema::drop('articles_tags');
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/database/migrations/2013_08_18_204038_create_users_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->string('email')->unique()->index();
19 | $table->string('password');
20 | $table->softDeletes();
21 | $table->timestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | *
28 | * @return void
29 | */
30 | public function down()
31 | {
32 | Schema::drop('users');
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/database/production.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/app/database/production.sqlite
--------------------------------------------------------------------------------
/app/database/seeds/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/app/database/seeds/.gitkeep
--------------------------------------------------------------------------------
/app/database/seeds/ArticleTableSeeder.php:
--------------------------------------------------------------------------------
1 | delete();
8 |
9 | /*
10 | Assumes:
11 |
12 | "published" has status_id = 1
13 | "draft" has status_id = 2
14 |
15 | "author" has user_id = 1
16 | */
17 |
18 | Article::create(array(
19 | 'user_id' => 1,
20 | 'status_id' => 1,
21 | 'title' => 'My first article',
22 | 'slug' => 'my-first-article',
23 | 'excerpt' => 'This is my first article, and here is a short description of it! Tally-ho!',
24 | 'content' => "This will be parsed as markdown and so needs some line-breaks.
25 |
26 | ## A h2 headline
27 | The content under-which is about the H2 headline, because Google knows everything about SEO and tells you have to build your html.",
28 | ));
29 |
30 | Article::create(array(
31 | 'user_id' => 1,
32 | 'status_id' => 1,
33 | 'title' => 'My second article',
34 | 'slug' => 'my-second-article',
35 | 'excerpt' => 'This is my second article, and here is a short description of said second article!',
36 | 'content' => "Synth nulla Banksy, sriracha odio ennui forage artisan keytar DIY. Meggings accusamus proident, meh ugh PBR single-origin coffee 3 wolf moon cliche twee dreamcatcher.
37 |
38 | ## Laborum thundercats gluten-free
39 | Terry Richardson ex semiotics mixtape wolf sunt proident salvia. Church-key Banksy bitters, ex mollit exercitation bicycle rights chambray gluten-free quis aute sriracha forage flexitarian vero.",
40 | ));
41 |
42 | Article::create(array(
43 | 'user_id' => 1,
44 | 'status_id' => 1,
45 | 'title' => 'My third article',
46 | 'slug' => 'my-third-article',
47 | 'excerpt' => 'This is my third article, and here is a short description of said third article!',
48 | 'content' => "Bacon ipsum dolor sit amet pork belly meatloaf ham hock jerky short ribs pastrami brisket ball tip swine ham fatback capicola spare ribs shank.
49 |
50 | ## Pancetta short ribs
51 | Pancetta jerky pork loin tenderloin, drumstick strip steak pork belly spare ribs fatback. Strip steak tongue sirloin pancetta tenderloin, ground round fatback sausage. Flank tenderloin beef shank jerky ham chuck jowl chicken. Kielbasa tenderloin beef ribs, capicola ham pancetta turducken shankle filet mignon pork loin.",
52 | ));
53 |
54 | Article::create(array(
55 | 'user_id' => 1,
56 | 'status_id' => 1,
57 | 'title' => 'My fourth article',
58 | 'slug' => 'my-fourth-article',
59 | 'excerpt' => 'This is my fourth article, and here is a short description of said fourth article!',
60 | 'content' => "Cliche quinoa swag roof party sartorial american apparel. Helvetica Brooklyn chambray PBR, intelligentsia scenester cupidatat 3 wolf moon food truck elit Pinterest ullamco master cleanse.
61 |
62 | Meh YOLO put a bird on it velit, minim banh mi non thundercats vegan enim sapiente irure assumenda photo booth. Aute Tonx flannel blog retro McSweeney's. Salvia ennui eu, fingerstache pickled blog twee minim polaroid authentic Brooklyn mixtape.",
63 | ));
64 |
65 | Article::create(array(
66 | 'user_id' => 1,
67 | 'status_id' => 2,
68 | 'title' => 'My greatest life achievement',
69 | 'slug' => 'my-greatest-life-achievement',
70 | 'excerpt' => "IT'S STILL A DRAFT! I HAVEN'T AHCIEVED ANYTHING!!!! I'VE FAILED AT YOLOING.",
71 | 'content' => "This is the story of a man, who is afraid. But then he just *crushes it* hardcore.",
72 | ));
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/app/database/seeds/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | call('UserTableSeeder');
15 | $this->call('StatusesTableSeeder');
16 | $this->call('ArticleTableSeeder');
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/app/database/seeds/StatusesTableSeeder.php:
--------------------------------------------------------------------------------
1 | delete();
8 |
9 | Status::create(array(
10 | 'status' => 'Published',
11 | 'slug' => 'published',
12 | ));
13 |
14 | Status::create(array(
15 | 'status' => 'Draft',
16 | 'slug' => 'draft',
17 | ));
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/app/database/seeds/UserTableSeeder.php:
--------------------------------------------------------------------------------
1 | delete();
8 |
9 | User::create(array(
10 | 'email' => 'fideloper@example.com',
11 | 'password' => Hash::make('password')
12 | ));
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/app/filters.php:
--------------------------------------------------------------------------------
1 | segment(1) === 'admin' )
17 | {
18 | Config::set("is_admin", true);
19 | } else {
20 | Config::set("is_admin", false);
21 | }
22 | });
23 |
24 |
25 | App::after(function($request, $response)
26 | {
27 | //
28 | });
29 |
30 | /*
31 | |--------------------------------------------------------------------------
32 | | Authentication Filters
33 | |--------------------------------------------------------------------------
34 | |
35 | | The following filters are used to verify that the user of the current
36 | | session is logged into this application. The "basic" filter easily
37 | | integrates HTTP Basic authentication for quick, simple checking.
38 | |
39 | */
40 |
41 | Route::filter('auth', function()
42 | {
43 | if (Auth::guest()) return Redirect::guest('login');
44 | });
45 |
46 |
47 | Route::filter('auth.basic', function()
48 | {
49 | return Auth::basic();
50 | });
51 |
52 | /*
53 | |--------------------------------------------------------------------------
54 | | Guest Filter
55 | |--------------------------------------------------------------------------
56 | |
57 | | The "guest" filter is the counterpart of the authentication filters as
58 | | it simply checks that the current user is not logged in. A redirect
59 | | response will be issued if they are, which you may freely change.
60 | |
61 | */
62 |
63 | Route::filter('guest', function()
64 | {
65 | if (Auth::check()) return Redirect::to('/');
66 | });
67 |
68 | /*
69 | |--------------------------------------------------------------------------
70 | | CSRF Protection Filter
71 | |--------------------------------------------------------------------------
72 | |
73 | | The CSRF filter is responsible for protecting your application against
74 | | cross-site request forgery attacks. If this special token in a user
75 | | session does not match the one given in this request, we'll bail.
76 | |
77 | */
78 |
79 | Route::filter('csrf', function()
80 | {
81 | if (Session::token() != Input::get('_token'))
82 | {
83 | throw new Illuminate\Session\TokenMismatchException;
84 | }
85 | });
--------------------------------------------------------------------------------
/app/lang/en/pagination.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 |
18 | 'next' => 'Next »',
19 |
20 | );
--------------------------------------------------------------------------------
/app/lang/en/reminders.php:
--------------------------------------------------------------------------------
1 | "Passwords must be six characters and match the confirmation.",
17 |
18 | "user" => "We can't find a user with that e-mail address.",
19 |
20 | "token" => "This password reset token is invalid.",
21 |
22 | );
--------------------------------------------------------------------------------
/app/lang/en/validation.php:
--------------------------------------------------------------------------------
1 | "The :attribute must be accepted.",
17 | "active_url" => "The :attribute is not a valid URL.",
18 | "after" => "The :attribute must be a date after :date.",
19 | "alpha" => "The :attribute may only contain letters.",
20 | "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.",
21 | "alpha_num" => "The :attribute may only contain letters and numbers.",
22 | "array" => "The :attribute must be an array.",
23 | "before" => "The :attribute must be a date before :date.",
24 | "between" => array(
25 | "numeric" => "The :attribute must be between :min - :max.",
26 | "file" => "The :attribute must be between :min - :max kilobytes.",
27 | "string" => "The :attribute must be between :min - :max characters.",
28 | "array" => "The :attribute must have between :min - :max items.",
29 | ),
30 | "confirmed" => "The :attribute confirmation does not match.",
31 | "date" => "The :attribute is not a valid date.",
32 | "date_format" => "The :attribute does not match the format :format.",
33 | "different" => "The :attribute and :other must be different.",
34 | "digits" => "The :attribute must be :digits digits.",
35 | "digits_between" => "The :attribute must be between :min and :max digits.",
36 | "email" => "The :attribute format is invalid.",
37 | "exists" => "The selected :attribute is invalid.",
38 | "image" => "The :attribute must be an image.",
39 | "in" => "The selected :attribute is invalid.",
40 | "integer" => "The :attribute must be an integer.",
41 | "ip" => "The :attribute must be a valid IP address.",
42 | "max" => array(
43 | "numeric" => "The :attribute may not be greater than :max.",
44 | "file" => "The :attribute may not be greater than :max kilobytes.",
45 | "string" => "The :attribute may not be greater than :max characters.",
46 | "array" => "The :attribute may not have more than :max items.",
47 | ),
48 | "mimes" => "The :attribute must be a file of type: :values.",
49 | "min" => array(
50 | "numeric" => "The :attribute must be at least :min.",
51 | "file" => "The :attribute must be at least :min kilobytes.",
52 | "string" => "The :attribute must be at least :min characters.",
53 | "array" => "The :attribute must have at least :min items.",
54 | ),
55 | "not_in" => "The selected :attribute is invalid.",
56 | "numeric" => "The :attribute must be a number.",
57 | "regex" => "The :attribute format is invalid.",
58 | "required" => "The :attribute field is required.",
59 | "required_if" => "The :attribute field is required when :other is :value.",
60 | "required_with" => "The :attribute field is required when :values is present.",
61 | "required_without" => "The :attribute field is required when :values is not present.",
62 | "same" => "The :attribute and :other must match.",
63 | "size" => array(
64 | "numeric" => "The :attribute must be :size.",
65 | "file" => "The :attribute must be :size kilobytes.",
66 | "string" => "The :attribute must be :size characters.",
67 | "array" => "The :attribute must contain :size items.",
68 | ),
69 | "unique" => "The :attribute has already been taken.",
70 | "url" => "The :attribute format is invalid.",
71 |
72 | /*
73 | |--------------------------------------------------------------------------
74 | | Custom Validation Language Lines
75 | |--------------------------------------------------------------------------
76 | |
77 | | Here you may specify custom validation messages for attributes using the
78 | | convention "attribute.rule" to name the lines. This makes it quick to
79 | | specify a specific custom language line for a given attribute rule.
80 | |
81 | */
82 |
83 | 'custom' => array(),
84 |
85 | /*
86 | |--------------------------------------------------------------------------
87 | | Custom Validation Attributes
88 | |--------------------------------------------------------------------------
89 | |
90 | | The following language lines are used to swap attribute place-holders
91 | | with something more reader friendly such as E-Mail Address instead
92 | | of "email". This simply helps us make messages a little cleaner.
93 | |
94 | */
95 |
96 | 'attributes' => array(),
97 |
98 | );
99 |
--------------------------------------------------------------------------------
/app/models/Article.php:
--------------------------------------------------------------------------------
1 | belongsTo('User');
42 | }
43 |
44 | /**
45 | * Define a one-to-one relationship.
46 | *
47 | * @return \Illuminate\Database\Eloquent\Relations\HasOne
48 | */
49 | public function status()
50 | {
51 | return $this->belongsTo('Status');
52 | }
53 |
54 | /**
55 | * Define a many-to-many relationship.
56 | *
57 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
58 | */
59 | public function tags()
60 | {
61 | return $this->belongsToMany('Tag', 'articles_tags', 'article_id', 'tag_id')->withTimestamps();
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/app/models/Status.php:
--------------------------------------------------------------------------------
1 | belongsToMany('Article', 'articles_tags', 'tag_id', 'article_id');
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/app/models/User.php:
--------------------------------------------------------------------------------
1 | getKey();
30 | }
31 |
32 | /**
33 | * Get the password for the user.
34 | *
35 | * @return string
36 | */
37 | public function getAuthPassword()
38 | {
39 | return $this->password;
40 | }
41 |
42 | /**
43 | * Get the e-mail address where password reminders are sent.
44 | *
45 | * @return string
46 | */
47 | public function getReminderEmail()
48 | {
49 | return $this->email;
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/app/routes.php:
--------------------------------------------------------------------------------
1 | 'admin'), function()
18 | {
19 | Route::resource('article', 'ArticleController');
20 | });
--------------------------------------------------------------------------------
/app/start/artisan.php:
--------------------------------------------------------------------------------
1 | client->request('GET', '/');
13 |
14 | $this->assertTrue($this->client->getResponse()->isOk());
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/app/tests/TestCase.php:
--------------------------------------------------------------------------------
1 |
2 | @if( count($errors->all()) )
3 |
4 | @foreach ($errors->all() as $error)
5 | - {{ $error }}
6 | @endforeach
7 |
8 | @endif
9 |
38 |
--------------------------------------------------------------------------------
/app/views/admin/article_edit.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @if( count($errors->all()) )
3 |
4 | @foreach ($errors->all() as $error)
5 | - {{ $error }}
6 | @endforeach
7 |
8 | @endif
9 |
40 |
--------------------------------------------------------------------------------
/app/views/admin/article_list.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | Articles
4 |
5 |
6 | ID |
7 | Title |
8 | Status |
9 | Published On |
10 | Updated On |
11 |
12 |
13 |
14 | @foreach($articles as $article)
15 |
16 | {{ $article->id }} |
17 | {{ $article->title }} |
18 | {{ $article->status->status }} |
19 | {{ $article->created_at }} |
20 | {{ $article->updated_at }} |
21 |
22 | @endforeach
23 |
24 |
25 |
26 |
27 | {{ $articles->links() }}
28 |
--------------------------------------------------------------------------------
/app/views/article.blade.php:
--------------------------------------------------------------------------------
1 |
2 | {{ $article->title }}
3 | {{ \Michelf\MarkdownExtra::defaultTransform($article->content) }}
4 |
--------------------------------------------------------------------------------
/app/views/emails/auth/reminder.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Password Reset
8 |
9 |
10 | To reset your password, complete this form: {{ URL::to('password/reset', array($token)) }}.
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/views/home.blade.php:
--------------------------------------------------------------------------------
1 | @foreach( $articles as $article )
2 |
3 |
4 | {{ \Michelf\MarkdownExtra::defaultTransform($article->excerpt) }}
5 |
6 | @endforeach
7 |
8 | {{ $articles->links() }}
9 |
--------------------------------------------------------------------------------
/app/views/layout.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Home | Implementing Laravel Blog
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
36 |
37 |
38 | {{ $content }}
39 |
40 |
41 |
49 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
70 |
71 |
73 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/views/pagination.php:
--------------------------------------------------------------------------------
1 |
4 |
5 | getLastPage() > 1): ?>
6 |
11 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | boot();
33 |
34 | /*
35 | |--------------------------------------------------------------------------
36 | | Load The Artisan Console Application
37 | |--------------------------------------------------------------------------
38 | |
39 | | We'll need to run the script to load and return the Artisan console
40 | | application. We keep this in its own script so that we will load
41 | | the console application independent of running commands which
42 | | will allow us to fire commands from Routes when we want to.
43 | |
44 | */
45 |
46 | $artisan = Illuminate\Console\Application::start($app);
47 |
48 | /*
49 | |--------------------------------------------------------------------------
50 | | Run The Artisan Application
51 | |--------------------------------------------------------------------------
52 | |
53 | | When we run the console application, the current CLI command will be
54 | | executed in this console and the response sent back to a terminal
55 | | or another output device for the developers. Here goes nothing!
56 | |
57 | */
58 |
59 | $status = $artisan->run();
60 |
61 | /*
62 | |--------------------------------------------------------------------------
63 | | Shutdown The Application
64 | |--------------------------------------------------------------------------
65 | |
66 | | Once Artisan has finished running. We will fire off the shutdown events
67 | | so that any final work may be done by the application before we shut
68 | | down the process. This is the last thing to happen to the request.
69 | |
70 | */
71 |
72 | $app->shutdown();
73 |
74 | exit($status);
--------------------------------------------------------------------------------
/bootstrap/autoload.php:
--------------------------------------------------------------------------------
1 | __DIR__.'/../app',
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Public Path
21 | |--------------------------------------------------------------------------
22 | |
23 | | The public path contains the assets for your web application, such as
24 | | your JavaScript and CSS files, and also contains the primary entry
25 | | point for web requests into these applications from the outside.
26 | |
27 | */
28 |
29 | 'public' => __DIR__.'/../public',
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Base Path
34 | |--------------------------------------------------------------------------
35 | |
36 | | The base path is the root of the Laravel installation. Most likely you
37 | | will not need to change this value. But, if for some wild reason it
38 | | is necessary you will do so here, just proceed with some caution.
39 | |
40 | */
41 |
42 | 'base' => __DIR__.'/..',
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Storage Path
47 | |--------------------------------------------------------------------------
48 | |
49 | | The storage path is used by Laravel to store cached Blade views, logs
50 | | and other pieces of information. You may modify the path here when
51 | | you want to change the location of this directory for your apps.
52 | |
53 | */
54 |
55 | 'storage' => __DIR__.'/../app/storage',
56 |
57 | );
58 |
--------------------------------------------------------------------------------
/bootstrap/start.php:
--------------------------------------------------------------------------------
1 | redirectIfTrailingSlash();
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Detect The Application Environment
21 | |--------------------------------------------------------------------------
22 | |
23 | | Laravel takes a dead simple approach to your application environments
24 | | so you can just specify a machine name or HTTP host that matches a
25 | | given environment, then we will automatically detect it for you.
26 | |
27 | */
28 |
29 | $env = $app->detectEnvironment(function()
30 | {
31 | return getenv('LARA_ENV') ?: 'development';
32 | });
33 |
34 | /*
35 | |--------------------------------------------------------------------------
36 | | Bind Paths
37 | |--------------------------------------------------------------------------
38 | |
39 | | Here we are binding the paths configured in paths.php to the app. You
40 | | should not be changing these here. If you need to change these you
41 | | may do so within the paths.php file and they will be bound here.
42 | |
43 | */
44 |
45 | $app->bindInstallPaths(require __DIR__.'/paths.php');
46 |
47 | /*
48 | |--------------------------------------------------------------------------
49 | | Load The Application
50 | |--------------------------------------------------------------------------
51 | |
52 | | Here we will load the Illuminate application. We'll keep this is in a
53 | | separate location so we can isolate the creation of an application
54 | | from the actual running of the application with a given request.
55 | |
56 | */
57 |
58 | $framework = $app['path.base'].'/vendor/laravel/framework/src';
59 |
60 | require $framework.'/Illuminate/Foundation/start.php';
61 |
62 | /*
63 | |--------------------------------------------------------------------------
64 | | Return The Application
65 | |--------------------------------------------------------------------------
66 | |
67 | | This script returns the application instance. The instance is given to
68 | | the calling script so we can separate the building of the instances
69 | | from the actual running of the application and sending responses.
70 | |
71 | */
72 |
73 | return $app;
74 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laravel/laravel",
3 | "description": "The Laravel Framework.",
4 | "keywords": ["framework", "laravel"],
5 | "require": {
6 | "laravel/framework": "4.0.*",
7 | "twilio/sdk": "dev-master",
8 | "michelf/php-markdown": "1.3.*@dev"
9 | },
10 | "autoload": {
11 | "classmap": [
12 | "app/commands",
13 | "app/controllers",
14 | "app/models",
15 | "app/database/migrations",
16 | "app/database/seeds",
17 | "app/tests/TestCase.php"
18 | ],
19 | "psr-0": {
20 | "Impl": "app"
21 | }
22 | },
23 | "scripts": {
24 | "post-install-cmd": [
25 | "php artisan optimize"
26 | ],
27 | "pre-update-cmd": [
28 | "php artisan clear-compiled"
29 | ],
30 | "post-update-cmd": [
31 | "php artisan optimize"
32 | ],
33 | "post-create-project-cmd": [
34 | "php artisan key:generate"
35 | ]
36 | },
37 | "config": {
38 | "preferred-install": "dist"
39 | },
40 | "minimum-stability": "dev"
41 | }
42 |
--------------------------------------------------------------------------------
/manuscript/000_thanks.txt:
--------------------------------------------------------------------------------
1 | {frontmatter}
2 |
3 | # Thanks
4 |
5 | *Thanks to Natalie for her patience, my reviewers for helping make this so much better and the team at Digital Surgeons for their support (and [Gumby](http://gumbyframework.com))!*
--------------------------------------------------------------------------------
/manuscript/001_who_is_this_for.txt:
--------------------------------------------------------------------------------
1 | -# Introduction
2 |
3 | # Who Is This For?
4 |
5 | ## Who Will Get the Most Benefit?
6 | This book is written for those who know the fundamentals of Laravel and are looking to see more advanced examples of implementing their knowledge in a testable and maintainable manner.
7 |
8 | From the lessons here, you will see how to apply architectural concepts to Laravel in a variety of ways, with the hope that you can use and adapt them for your own needs.
9 |
10 | ## What To Know Ahead of Time
11 | We all have varying levels of knowledge. This book is written for those who are familiar with Laravel 4 and its core concepts. It therefore assumes some knowledge of the reader.
12 |
13 | Taylor Otwell's book [*Laravel: From Apprentice to Artisan*](https://leanpub.com/laravel) is a great prerequisite. Although I'll cover these on a basic level, readers will hopefully already have a basic understanding of the principles of SOLID and Laravel's IoC container.
--------------------------------------------------------------------------------
/manuscript/002-5_solid.txt:
--------------------------------------------------------------------------------
1 | {mainmatter}
2 |
3 | # SOLID
4 |
5 | Since I'll mention SOLID principles in passing throughout this book, I'll include a very brief explanation of them here, mostly taken from the [Wikipedia entry](http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)) with some extra explanation in context of Laravel.
6 |
7 | ### Single Responsibility Principle
8 |
9 | A class (or unit of code) should have one responsibility.
10 |
11 | ### Open/Closed Principle
12 |
13 | A class should be open for extension but closed for modification.
14 |
15 | You can extend a class or implement and interface, but you should not be able to modify a class directly. This means you should extend a class and use the new extension rather than change a class directly.
16 |
17 | Additionally, this means setting class attributes and methods as private or protected properly so they cannot be modified by external code.
18 |
19 | ### Liskov Substitution Principle
20 |
21 | Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
22 |
23 | In PHP, this often means creating interfaces for your code to implement. You can then change (switch-out) implementations of the interfaces. Doing so should be possible without having to change how your application code interacts with the implementation. The interface serves as a contract, guaranteeing that certain methods will be available.
24 |
25 | ### Interface Segregation Principle
26 |
27 | Many client-specific interfaces are better than one general-purpose interface.
28 |
29 | In general, it's preferable to create an interface and implement it many times over than create a general-purpose class which attempts to work in all situations.
30 |
31 | ### Dependency Inversion Principle
32 |
33 | One should depend upon abstractions rather than concrete classes.
34 |
35 | You should define class dependencies as an interface rather than a concrete class. This allows you to switch an implementation of the interface out without having to change the class using the dependency.
36 |
--------------------------------------------------------------------------------
/manuscript/002_a_note_on_opinions.txt:
--------------------------------------------------------------------------------
1 | # A Note on Opinions
2 |
3 | Knowing the benefits (and pitfalls!) of Repository, Dependency Injection, Container, Service Locator patterns and other tools from our architectural tool set can be both liberating and exciting.
4 |
5 | The use of those tools, however, can be plagued with unexpected and often nuanced issues.
6 |
7 | As such, there are many opinions about how to go about crafting "good code" with such tools.
8 |
9 | As I use many real examples in this book, I have implicitly (and sometimes explicitly!) included my own opinions in this book. Always, however, inform your own opinion with both what you read and your own experience!
10 |
11 | W> ## "When all you have is a hammer..."
12 | W>
13 | W> Overuse of any of these tools can cause it's own issues. The chapters here are examples of how you *can* implement the architectural tools available to us. Knowing when *not* to use them is also an important decision to keep in mind.
--------------------------------------------------------------------------------
/manuscript/005_the_sample_application.txt:
--------------------------------------------------------------------------------
1 | -# Setting Up Laravel
2 |
3 | # The Sample Application
4 |
5 | This book will use a sample application. We will build upon a simple blog application - everybody's favorite weekend learning project.
6 |
7 | The application is viewable on Github at [fideloper\Implementing-Laravel](https://github.com/fideloper/implementing-laravel). There you can view code and see working examples from this book.
8 |
9 | Here is an overview of some information about the application.
10 |
11 | ## Database
12 |
13 | We'll have a database with the following tables and fields:
14 |
15 | * **articles** - id, user_id, status_id, title, slug, excerpt, content, created_at, updated_at, deleted_at
16 | * **statuses** - id, status, slug, created_at, updated_at
17 | * **tags** - id, tag, slug
18 | * **articles_tags** - article_id, tag_id
19 | * **users** - id, email, password, created_at, updated_at, deleted_at
20 |
21 | You'll find migrations for these tables, as well as some seeds, in the `app/database/migrations` directory on Github.
22 |
23 | ## Models
24 |
25 | Each of the database tables will have a corresponding Eloquent model class, inside of the `app/models` directory.
26 |
27 | * `models/Article.php`
28 | * `models/Status.php`
29 | * `models/Tag.php`
30 | * `models/User.php`
31 |
32 | ## Relationships
33 |
34 | Users can create articles and so there is a "one-to-many" relationship between users and articles; Each user can write multiple articles, but each article only has one user. This relationship is created within both the `User` and `Article` models.
35 |
36 | Like users, there is a "one to many" relationship between statuses and articles - Each article is assigned a status, and a status can be assigned to many articles.
37 |
38 | Finally, the Articles and Tags have a relationship. Each article can have one or many tags. Each tag can be assigned to one or many articles. Therefore, there is a "many to many" relationship between articles and tags. This relationship is defined within the `Article` and `Tag` models, and uses the `Articles_Tags` pivot table.
39 |
40 | ## Testability and Maintainability
41 |
42 | I'll use the phrase "testable and maintainable" a lot. In most contexts, you can assume:
43 |
44 | 1. **Testable** code practices SOLID principles in a way that allows us to unit test - to test one specific unit of code (or class) without bringing in its dependencies. This is most notable in the use of Dependency Injection which directly allows the use of mocking to abstract away class dependencies.
45 | 2. **Maintainable** code looks to the long-term cost in development time. This means that making changes to the code should be easy, even after months or years. Popular examples of a change that should be easy is switching out one email provider for another or one data-store for another. This is most directly realized through the use of interfaces and making use of inversion of control.
46 |
47 | ## Architectural Notes
48 | This book will cover creating an application library which contains most application code for the sample blog. The structure of the application library makes some assumptions about how we go about building the application code.
49 |
50 | First, you'll note that I do not put Controllers into my application code. This is on purpose!
51 |
52 | Laravel is a web-framework, designed to handle an HTTP request and route to your application code. The Request, Router and Controller classes are all designed to operate at the HTTP level.
53 |
54 | Our application, however, does not need any such requirement. We're simply writing application logic which revolves around our business goals.
55 |
56 | An HTTP request being routed to a controller function can be seen as a convenient way for a request to reach our application code (we're all on the internet, after all). However, our application does not necessarily need to "know" about the code calling our application, or HTTP at all.
57 |
58 | This is an extension of the concept "separation of concerns". While we certainly are unlikely to use our applications out of context of the internet, it is useful to think of your web framework as an implementation detail of your application, rather than the core of it.
59 |
60 | Think of it this way: our applications are not an implementation of the Laravel framework. Instead, Laravel is an implementation of our applications.
61 |
62 | Taken to an extreme, an application would be able to be implemented by any framework or code capable of implementing the interfaces we define.
63 |
64 | Extremes, however, are not pragmatic, and so we use Laravel to accomplish most of our application goals. Laravel is the means to most of our ends - it does things extremely well!
65 |
66 | This also shapes the application library structure I build up to in this book. I'll create a series of directories reflecting application code functions, such as a data repository and cache services. These functional areas tend to be interfaced, which we implement using various Illuminate packages.
--------------------------------------------------------------------------------
/manuscript/007_application_setup.txt:
--------------------------------------------------------------------------------
1 | # Application Setup
2 |
3 | If you've ever asked yourself "Where should I put my code?", this chapter will answer that question.
4 |
5 | **Always** create an application library.
6 |
7 | An application-specific library works well for code which isn't generic enough to warrant its own package, and also isn't a direct use of Laravel's core classes, such as a controller. An example is business logic code or integration of third-party libraries.
8 |
9 | Basically, anything that you want to keep out of your controllers (most code!) should belong in your application library.
10 |
11 | ## Setting Up the Application Library
12 |
13 | To accomplish this, we'll start by creating a namespace for the application. In our example application, I'll choose the easy-to-type name "Impl", short for "Implementing Laravel". This will be both our application's top-level namespace and the directory name. We will create the directory `app/Impl` to house this application code.
14 |
15 | Here's what the folder structure will look like:
16 |
17 | Implementing Laravel
18 | |- app
19 | |--- commands
20 | |--- config
21 | |--- [ and so on ]
22 | |--- Impl
23 | |------ Exception
24 | |------ Repo
25 | |------ Service
26 |
27 |
28 | As you likely know, the namespace, file name and directory structure matters - they allow us to autoload the PHP files based on the [PSR-0 autoloading standard](http://www.php-fig.org/psr/0/).
29 |
30 | For example, a sample class in this structure might look like this:
31 |
32 | {title="File: app/Impl/Repo/EloquentArticle.php", lang=php}
33 | Using [Composer's dump-autoload](http://getcomposer.org/doc/03-cli.md#dump-autoload) is a way of telling Composer to find new classes in a classmap package (Laravel's controllers, models, etc). For PSR-0 autoloading, it can also rebuild composer's optimized autoloader, saving time when the application is run.
74 |
75 | Now anywhere in our code, we can instantiate the `Impl\Repo\EloquentArticle` class and PHP will know to autoload it!
76 |
77 | {title="File: app/routes.php"}
78 | Route::get('/', function()
79 | {
80 | $articleRepo = new Impl\Repo\EloquentArticle;
81 |
82 | return View::make('home')->with('articles', $articleRepo->all());
83 | }
84 |
85 | ## Wrapping Up
86 |
87 | We saw how to create a separate application library to house our application code. This library is where our business logic, extensions, IoC bindings and more will be added.
88 |
89 | T>## Location, Location, Location
90 | T>
91 | T> Your application library can go anywhere. By convention, I add mine into the `app` directory, but you are certainly not limited to that location. You may want to use another location, or even consider creating a package out of your application library that can be added as a Composer dependency!
--------------------------------------------------------------------------------
/manuscript/014_conclusion.txt:
--------------------------------------------------------------------------------
1 | -# Conclusion
2 |
3 | # Review
4 |
5 | We covered a lot of ground in this book! Some of the topics included:
6 |
7 | ## Installation
8 |
9 | Installing Laravel, including environment setup and production considerations.
10 |
11 | ## Application Setup
12 |
13 | Using an application library.
14 |
15 | ## Repository Pattern
16 |
17 | The how and why of using repositories as an interface to your data storage.
18 |
19 | ## Caching In the Repository
20 |
21 | We added a service layer into our code repository. In this example, we added a layer of caching.
22 |
23 | ## Validation
24 |
25 | We created validation as a service, helping us abstract out the work of validation and implement validation classes specific to our needed use cases.
26 |
27 | ## Form Processing
28 |
29 | We created classes for orchestrating the validation of input and the interaction with our data repositories. These form classes are decoupled from HTTP requests and are able to be used outside of a controller. This also makes them easier to test.
30 |
31 | ## Error Handling
32 |
33 | We went over some considerations of how and when to use error handling, and saw an example of how it might be done to catch application-specific errors.
34 |
35 | ## Third Party Libraries
36 |
37 | We used Twilio's SDK to build a notification library so could send SMS notifications within our error handler.
38 |
39 | # What Did You Gain?
40 |
41 | Most of this book lays the ground work for building a full featured application.
42 |
43 | What I hope you gained from reading this is insight on how SOLID principles can be used in Laravel. The use of interfaces, the IoC container and the Service Providers all provide a powerful way to create a highly testable and maintainable PHP application.
44 |
45 | ## The Future
46 |
47 | I'm hoping the early editions of this book serve as a base on which to build.
48 |
49 | The answer to many questions such as "How do I use queues effectively?", "How do I integrate a search engine?" or "What's an advanced usage of Service Providers" will rely and expand on the fundamentals covered here.
50 |
51 | With any luck, this will be just the beginning.
52 |
53 | Happy coding!
--------------------------------------------------------------------------------
/manuscript/Book.txt:
--------------------------------------------------------------------------------
1 | 000_thanks.txt
2 | 001_who_is_this_for.txt
3 | 002_a_note_on_opinions.txt
4 | 002-5_solid.txt
5 | 003_the_container.txt
6 | 004_dependency_injection.txt
7 | 005_the_sample_application.txt
8 | 006_installation.txt
9 | 007_application_setup.txt
10 | 008_repository_pattern.txt
11 | 009_repository_pattern_cacheing.txt
12 | 010_validation.txt
13 | 011_form_processing.txt
14 | 012_error_handling.txt
15 | 013_notifications.txt
16 | 014_conclusion.txt
--------------------------------------------------------------------------------
/manuscript/Preview.txt:
--------------------------------------------------------------------------------
1 | 003_the_container.txt
2 | 004_dependency_injection.txt
3 |
--------------------------------------------------------------------------------
/manuscript/Sample.txt:
--------------------------------------------------------------------------------
1 | 003_the_container.txt
2 | 004_dependency_injection.txt
3 |
--------------------------------------------------------------------------------
/manuscript/images/title_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/manuscript/images/title_page.png
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./app/tests/
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 | SetEnv LARA_ENV local
2 |
3 |
4 | Options -MultiViews
5 | RewriteEngine On
6 |
7 | RewriteCond %{REQUEST_FILENAME} !-d
8 | RewriteCond %{REQUEST_FILENAME} !-f
9 | RewriteRule ^ index.php [L]
10 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | /*
2 | We highly recommend you use SASS and write your custom styles in sass/_custom.scss.
3 | However, this blank file is available if you prefer
4 | */
5 |
--------------------------------------------------------------------------------
/public/facebook.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/facebook.jpg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/favicon.ico
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/favicon.png
--------------------------------------------------------------------------------
/public/fonts/icons/entypo.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/fonts/icons/entypo.eot
--------------------------------------------------------------------------------
/public/fonts/icons/entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/fonts/icons/entypo.ttf
--------------------------------------------------------------------------------
/public/fonts/icons/entypo.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/fonts/icons/entypo.woff
--------------------------------------------------------------------------------
/public/humans.txt:
--------------------------------------------------------------------------------
1 | /* TEAM */
2 |
3 | Digital Surgeons
4 | Twitter: @digitalsurgeons
5 | Twitter: @gumbycss
6 | Web: www.digitalsurgeons.com
7 | Web: www.gumbyframework.com
8 |
9 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
10 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
11 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
12 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,:~~~====~,,,,,,,,,,,,,
13 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,=================+,,,,,,,,,,,,,
14 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,:==================,,,,,,,,,,,,,
15 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+=================:,,,,,,,,,,,,
16 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,==================:,,,,,,,,,,,,
17 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,~=================~,,,,,,,,,,,,
18 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,~=================~,,,,,,,,,,,,
19 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,=================~,,,,,,,,,,,,
20 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,==================,,,,,,,,,,,,
21 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,==================,,,,,,,,,,,,
22 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,==================,,,,,,,,,,,,
23 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,==================,,,,,,,,,,,,
24 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,==================,,,,,,,,,,,,
25 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,~==============:,,,,,,,,,,,,,,
26 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,==========:,,,,,,,,,,,,,,,,,,,
27 | ,,,,,,,,,,,,,,,,,,,,,,,,:~=========:,,,,,,,,,,,,,,,,,,,,,,,,
28 | ,,,,,,,,,,,,,,,,,:================,,,,,,,,,,,,,,,,,,,,,,,,,,
29 | ,,,,,,,,,,,,:=====================,,,,,,,,,,,,,,,,,,,,,,,,,,
30 | ,,,,,,,,,,,=======================,,,,,,,,,,,,,,,,,,,,,,,,,,
31 | ,,,,,,,,,,,=======================:,,,,,,,,,,,,,,,,,,,,,,,,,
32 | ,,,,,,,,,,,=======================~,,,,,,,,,,,,,,,,,,,,,,,,,
33 | ,,,,,,,,,,,,=~====================~,,,,,,,,,,,,,,,,,,,,,,,,,
34 | ,,,,,,,,,,,,=~~~~~~~~~~~~~~~~~~~~~=,,,,,,,,,,,,,,,,,,,,,,,,,
35 | ,,,,,,,,,,,,~~==~~~~~~~~~~~~~~=====,,,,,,,,,,,,,,,,,,,,,,,,,
36 | ,,,,,,,,,,,,,=~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,,,,,,,,
37 | ,,,,,,,,,,,,,=~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,,,,,,,,
38 | ,,,,,,,,,,,,,,~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,,,,,,,,,
39 | ,,,,,,,,,,,,,,~~~~~~~~~~~~~~~~:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
40 | ,,,,,,,,,,,,,,~~~~~~~~~~~~~:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
41 | ,,,,,,,,,,,,,,,~~~~~~~~,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
42 | ,,,,,,,,,,,,,,,~~~~:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
43 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
44 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..
45 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,....
46 | ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.....
47 |
--------------------------------------------------------------------------------
/public/img/gumby_mainlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/img/gumby_mainlogo.png
--------------------------------------------------------------------------------
/public/img/gumby_mainlogo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/img/gumby_mainlogo@2x.png
--------------------------------------------------------------------------------
/public/img/img_silence_demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/img/img_silence_demo.jpg
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | /*
10 | |--------------------------------------------------------------------------
11 | | Register The Auto Loader
12 | |--------------------------------------------------------------------------
13 | |
14 | | Composer provides a convenient, automatically generated class loader
15 | | for our application. We just need to utilize it! We'll require it
16 | | into the script here so that we do not have to worry about the
17 | | loading of any our classes "manually". Feels great to relax.
18 | |
19 | */
20 |
21 | require __DIR__.'/../bootstrap/autoload.php';
22 |
23 | /*
24 | |--------------------------------------------------------------------------
25 | | Turn On The Lights
26 | |--------------------------------------------------------------------------
27 | |
28 | | We need to illuminate PHP development, so let's turn on the lights.
29 | | This bootstraps the framework and gets it ready for use, then it
30 | | will load up this application so that we can run it and send
31 | | the responses back to the browser and delight these users.
32 | |
33 | */
34 |
35 | $app = require_once __DIR__.'/../bootstrap/start.php';
36 |
37 | /*
38 | |--------------------------------------------------------------------------
39 | | Run The Application
40 | |--------------------------------------------------------------------------
41 | |
42 | | Once we have the application, we can simply call the run method,
43 | | which will execute the request and send the response back to
44 | | the client's browser allowing them to enjoy the creative
45 | | and wonderful applications we have created for them.
46 | |
47 | */
48 |
49 | $app->run();
50 |
51 | /*
52 | |--------------------------------------------------------------------------
53 | | Shutdown The Application
54 | |--------------------------------------------------------------------------
55 | |
56 | | Once the app has finished running, we will fire off the shutdown events
57 | | so that any final work may be done by the application before we shut
58 | | down the process. This is the last thing to happen to the request.
59 | |
60 | */
61 |
62 | $app->shutdown();
--------------------------------------------------------------------------------
/public/js/libs/gumby.init.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby Init
3 | */
4 |
5 | // test for touch event support
6 | Modernizr.load({
7 | test: Modernizr.touch,
8 |
9 | // if present load custom jQuery mobile build and update Gumby.click
10 | yep: Gumby.path+'/jquery.mobile.custom.min.js',
11 | callback: function(url, result, key) {
12 | // check jQuery mobile has successfully loaded before using tap events
13 | if($.mobile) {
14 | window.Gumby.click += ' tap';
15 | }
16 | },
17 |
18 | // either way initialize Gumby
19 | complete: function() {
20 | window.Gumby.init();
21 |
22 | // if AMD return Gumby object to define
23 | if(typeof define == "function" && define.amd) {
24 | define(window.Gumby);
25 | }
26 | }
27 | });
28 |
--------------------------------------------------------------------------------
/public/js/libs/gumby.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby Framework
3 | * ---------------
4 | *
5 | * Follow @gumbycss on twitter and spread the love.
6 | * We worked super hard on making this awesome and released it to the web.
7 | * All we ask is you leave this intact. #gumbyisawesome
8 | *
9 | * Gumby Framework
10 | * http://gumbyframework.com
11 | *
12 | * Built with love by your friends @digitalsurgeons
13 | * http://www.digitalsurgeons.com
14 | *
15 | * Free to use under the MIT license.
16 | * http://www.opensource.org/licenses/mit-license.php
17 | */
18 | !function() {
19 |
20 | 'use strict';
21 |
22 | function Gumby() {
23 | this.$dom = $(document);
24 | this.isOldie = !!this.$dom.find('html').hasClass('oldie');
25 | this.click = 'click';
26 | this.onReady = this.onOldie = this.onTouch = false;
27 | this.uiModules = {};
28 | this.inits = {};
29 |
30 | // check and set path with js/libs default
31 | this.path = $('script[gumby-path]').attr('gumby-path') || 'js/libs';
32 |
33 | // check and set breakpoint with 1024 default
34 | this.breakpoint = Number($('script[gumby-breakpoint]').attr('gumby-breakpoint')) || 1024;
35 | }
36 |
37 | // initialize Gumby
38 | Gumby.prototype.init = function() {
39 | var scope = this;
40 |
41 | // call ready() code when dom is ready
42 | this.$dom.ready(function() {
43 | // init UI modules
44 | scope.initUIModules();
45 |
46 | if(scope.onReady) {
47 | scope.onReady();
48 | }
49 |
50 | // call oldie() callback if applicable
51 | if(scope.isOldie && scope.onOldie) {
52 | scope.onOldie();
53 | }
54 |
55 | // call touch() callback if applicable
56 | if(Modernizr.touch && scope.onTouch) {
57 | scope.onTouch();
58 | }
59 | });
60 | };
61 |
62 | // public helper - set Gumby ready callback
63 | Gumby.prototype.ready = function(code) {
64 | if(code && typeof code === 'function') {
65 | this.onReady = code;
66 | }
67 | };
68 |
69 | // public helper - set oldie callback
70 | Gumby.prototype.oldie = function(code) {
71 | if(code && typeof code === 'function') {
72 | this.onOldie = code;
73 | }
74 | };
75 |
76 | // public helper - set touch callback
77 | Gumby.prototype.touch = function(code) {
78 | if(code && typeof code === 'function') {
79 | this.onTouch = code;
80 | }
81 | };
82 |
83 | // public helper - return debuggin object including uiModules object
84 | Gumby.prototype.debug = function() {
85 | return {
86 | $dom: this.$dom,
87 | isOldie: this.isOldie,
88 | uiModules: this.uiModules,
89 | click: this.click
90 | };
91 | };
92 |
93 | // grab attribute value, testing data- gumby- and no prefix
94 | Gumby.prototype.selectAttr = function() {
95 | var i = 0;
96 |
97 | // any number of attributes can be passed
98 | for(; i < arguments.length; i++) {
99 | // various formats
100 | var attr = arguments[i],
101 | dataAttr = 'data-'+arguments[i],
102 | gumbyAttr = 'gumby-'+arguments[i];
103 |
104 | // first test for data-attr
105 | if(this.is('['+dataAttr+']')) {
106 | return this.attr(dataAttr) ? this.attr(dataAttr) : true;
107 |
108 | // next test for gumby-attr
109 | } else if(this.is('['+gumbyAttr+']')) {
110 | return this.attr(gumbyAttr) ? this.attr(gumbyAttr) : true;
111 |
112 | // finally no prefix
113 | } else if(this.is('['+attr+']')) {
114 | return this.attr(attr) ? this.attr(attr) : true;
115 | }
116 | }
117 |
118 | // none found
119 | return false;
120 | };
121 |
122 | // add an initialisation method
123 | Gumby.prototype.addInitalisation = function(ref, code) {
124 | this.inits[ref] = code;
125 | };
126 |
127 | // initialize a uiModule
128 | Gumby.prototype.initialize = function(ref, all) {
129 | if(this.inits[ref] && typeof this.inits[ref] === 'function') {
130 | this.inits[ref](all);
131 | }
132 | };
133 |
134 | // store a UI module
135 | Gumby.prototype.UIModule = function(data) {
136 | var module = data.module;
137 | this.uiModules[module] = data;
138 | };
139 |
140 | // loop round and init all UI modules
141 | Gumby.prototype.initUIModules = function() {
142 | var x;
143 | for(x in this.uiModules) {
144 | this.uiModules[x].init();
145 | }
146 | };
147 |
148 | window.Gumby = new Gumby();
149 |
150 | }();
151 |
--------------------------------------------------------------------------------
/public/js/libs/ui/gumby.checkbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby Checkbox
3 | */
4 | !function() {
5 |
6 | 'use strict';
7 |
8 | function Checkbox($el) {
9 |
10 | this.$el = $el;
11 | this.$input = this.$el.find('input[type=checkbox]');
12 |
13 | var scope = this;
14 |
15 | // listen for click event and custom gumby check/uncheck events
16 | this.$el.on(Gumby.click, function(e) {
17 | // prevent propagation
18 | e.stopImmediatePropagation();
19 |
20 | // prevent checkbox checking, we'll do that manually
21 | e.preventDefault();
22 |
23 | // do nothing if checkbox is disabled
24 | if(scope.$input.is('[disabled]')) {
25 | return;
26 | }
27 |
28 | // check/uncheck
29 | if(scope.$el.hasClass('checked')) {
30 | scope.update(false);
31 | } else {
32 | scope.update(true);
33 | }
34 | }).on('gumby.check', function() {
35 | scope.update(true);
36 | }).on('gumby.uncheck', function() {
37 | scope.update(false);
38 | });
39 |
40 | // update any .checked checkboxes on load
41 | if(scope.$el.hasClass('checked')) {
42 | scope.update(true);
43 | }
44 | }
45 |
46 | // update checkbox, check equals true/false to sepcify check/uncheck
47 | Checkbox.prototype.update = function(check) {
48 |
49 | var $span = this.$el.find('span');
50 |
51 | // check checkbox - check input, add checked class, append
52 | if(check) {
53 |
54 | $span.append('');
55 |
56 | this.$input.prop('checked', true).end()
57 | .addClass('checked')
58 | .trigger('gumby.onCheck').trigger('gumby.onChange');
59 |
60 | // uncheck checkbox - uncheck input, remove checked class, remove
61 | } else {
62 | this.$input.prop('checked', false).end()
63 | .find('i').remove().end()
64 | .removeClass('checked').trigger('gumby.onUncheck').trigger('gumby.onChange');
65 | }
66 | };
67 |
68 | // add initialisation
69 | Gumby.addInitalisation('checkboxes', function() {
70 | $('.checkbox').each(function() {
71 | var $this = $(this);
72 | // this element has already been initialized
73 | if($this.data('isCheckbox')) {
74 | return true;
75 | }
76 | // mark element as initialized
77 | $this.data('isCheckbox', true);
78 | new Checkbox($this);
79 | });
80 | });
81 |
82 | // register UI module
83 | Gumby.UIModule({
84 | module: 'checkbox',
85 | events: ['onCheck', 'onUncheck', 'onChange', 'check', 'uncheck'],
86 | init: function() {
87 | Gumby.initialize('checkboxes');
88 | }
89 | });
90 | }();
91 |
--------------------------------------------------------------------------------
/public/js/libs/ui/gumby.fittext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby FitText
3 | *
4 | * Adapted from the awesome FitText jQuery plugin
5 | * brought to you by Paravel - http://paravelinc.com/
6 | */
7 | !function() {
8 |
9 | 'use strict';
10 |
11 | function FitText($el) {
12 | this.$el = $el;
13 |
14 | this.rate = 0;
15 | this.fontSizes = {};
16 |
17 | // set up module based on attributes
18 | this.setup();
19 |
20 | var scope = this;
21 |
22 | // re-initialize module
23 | this.$el.on('gumby.initialize', function() {
24 | scope.setup();
25 | });
26 |
27 | // lets go
28 | $(window).on('load resize orientationchange', function() {
29 | scope.resize();
30 | });
31 | }
32 |
33 | // set up module based on attributes
34 | FitText.prototype.setup = function() {
35 | // optional compressor rate
36 | this.rate = Number(Gumby.selectAttr.apply(this.$el, ['rate'])) || 1;
37 | // optional font sizes (min|max)
38 | this.fontSizes = this.parseSizes(Gumby.selectAttr.apply(this.$el, ['sizes']));
39 | };
40 |
41 | // apply the resizing
42 | FitText.prototype.resize = function() {
43 | this.$el.css('font-size', this.calculateSize());
44 | };
45 |
46 | // calculate the font size
47 | FitText.prototype.calculateSize = function() {
48 | return Math.max(Math.min(this.$el.width() / (this.rate*10), parseFloat(this.fontSizes.max)), parseFloat(this.fontSizes.min));
49 | };
50 |
51 | // parse size attributes with min|max syntax
52 | FitText.prototype.parseSizes = function(attrStr) {
53 | var sizes = {
54 | min: Number.NEGATIVE_INFINITY,
55 | max: Number.POSITIVE_INFINITY
56 | };
57 |
58 | // attribute is optional
59 | if(!attrStr) { return sizes; }
60 |
61 | // min and/or max specified
62 | if(attrStr.indexOf('|') > -1) {
63 | attrStr = attrStr.split('|');
64 |
65 | // both are optional
66 | sizes.min = Number(attrStr[0]) || sizes.min;
67 | sizes.max = Number(attrStr[1]) || sizes.max;
68 | }
69 |
70 | // only one value specific without | so use as min
71 | sizes.min = Number(attrStr) || sizes.min;
72 |
73 | return sizes;
74 | };
75 |
76 | // add initialisation
77 | Gumby.addInitalisation('fittext', function(all) {
78 | $('.fittext').each(function() {
79 | var $this = $(this);
80 |
81 | // this element has already been initialized
82 | // and we're only initializing new modules
83 | if($this.data('isFittext') && !all) {
84 | return true;
85 |
86 | // this element has already been initialized
87 | // and we need to reinitialize it
88 | } else if($this.data('isFittext') && all) {
89 | $this.trigger('gumby.initialize');
90 | return true;
91 | }
92 |
93 | // mark element as initialized
94 | $this.data('isFittext', true);
95 | new FitText($this);
96 | });
97 | });
98 |
99 | // register UI module
100 | Gumby.UIModule({
101 | module: 'fittext',
102 | events: [],
103 | init: function() {
104 | Gumby.initialize('fittext');
105 | }
106 | });
107 | }();
108 |
--------------------------------------------------------------------------------
/public/js/libs/ui/gumby.navbar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby Navbar
3 | */
4 | !function() {
5 |
6 | 'use strict';
7 |
8 | var $html = Gumby.$dom.find('html');
9 |
10 | // define and init module on touch enabled devices only
11 | // when we are at tablet size or smaller
12 | if(!Modernizr.touch || $(window).width() > Gumby.breakpoint) {
13 |
14 | // add Gumby no touch class
15 | $html.addClass('gumby-no-touch');
16 | return;
17 | }
18 |
19 | // add Gumby touch class
20 | $html.addClass('gumby-touch');
21 |
22 | function Navbar($el) {
23 | this.$el = $el;
24 | this.$dropDowns = this.$el.find('li:has(.dropdown)');
25 | var scope = this;
26 |
27 | // when navbar items
28 | this.$dropDowns
29 | // are tapped hide/show dropdowns
30 | .on('tap', this.toggleDropdown)
31 | // are swiped right open link
32 | .on('swiperight', this.openLink);
33 |
34 | // if there's a link set
35 | if(this.$dropDowns.children('a').attr('href') !== '#') {
36 | // append an icon
37 | this.$dropDowns.children('a').append('').children('i')
38 | // and bind to click event to open link
39 | .on('tap', this.openLink);
40 | }
41 |
42 | // override with childlinks
43 | this.$dropDowns.find('.dropdown li:not(:has(.dropdown)) a[href]').on('tap', this.openLink);
44 |
45 | // on mousemove and touchstart toggle modernizr classes and disable/enable this module
46 | // workaround for Pixel and other multi input devices
47 | $(window).on('mousemove touchstart', function(e) {
48 | e.stopImmediatePropagation();
49 | if(e.type === 'mousemove') {
50 | scope.$dropDowns.on('mouseover mouseout', scope.toggleDropdown);
51 | }
52 | });
53 | }
54 |
55 | Navbar.prototype.toggleDropdown = function(e) {
56 | // prevent click from triggering here too
57 | e.stopImmediatePropagation();
58 | e.preventDefault();
59 |
60 | var $this = $(this);
61 |
62 | if($this.hasClass('active')) {
63 | $this.removeClass('active');
64 | } else {
65 | $this.addClass('active');
66 | }
67 | };
68 |
69 | // handle opening list item link
70 | Navbar.prototype.openLink = function(e) {
71 | e.stopImmediatePropagation();
72 | e.preventDefault();
73 |
74 | var $this = $(this),
75 | $el = $this, href;
76 |
77 | // tapped icon
78 | if($this.is('i')) {
79 | $el = $this.parent('a');
80 | // swiped li
81 | } else if($this.is('li')) {
82 | $el = $this.children('a');
83 | }
84 |
85 | href = $el.attr('href');
86 |
87 | // open in new window
88 | if($el.attr('target') == 'blank') {
89 | window.open(href);
90 | // regular relocation
91 | } else {
92 | window.location = href;
93 | }
94 | };
95 |
96 | // add initialisation
97 | Gumby.addInitalisation('navbars', function() {
98 | $('.navbar').each(function() {
99 | var $this = $(this);
100 | // this element has already been initialized
101 | if($this.data('isNavbar')) {
102 | return true;
103 | }
104 | // mark element as initialized
105 | $this.data('isNavbar', true);
106 | new Navbar($this);
107 | });
108 | });
109 |
110 | // register UI module
111 | Gumby.UIModule({
112 | module: 'navbar',
113 | events: [],
114 | init: function() {
115 | Gumby.initialize('navbars');
116 | }
117 | });
118 | }();
119 |
--------------------------------------------------------------------------------
/public/js/libs/ui/gumby.radiobtn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby RadioBtn
3 | */
4 | !function() {
5 |
6 | 'use strict';
7 |
8 | function RadioBtn($el) {
9 |
10 | this.$el = $el;
11 | this.$input = this.$el.find('input[type=radio]');
12 |
13 | var scope = this;
14 |
15 | // listen for click event and custom gumby check event
16 | this.$el.on(Gumby.click, function(e) {
17 | // prevent propagation
18 | e.stopImmediatePropagation();
19 |
20 | // prevent radio button checking, we'll do that manually
21 | e.preventDefault();
22 |
23 | // do nothing if radio is disabled
24 | if (scope.$input.is('[disabled]')) {
25 | return;
26 | }
27 |
28 | // check radio button
29 | scope.update();
30 | }).on('gumby.check', function() {
31 | scope.update();
32 | });
33 |
34 | // update any .checked checkboxes on load
35 | if(scope.$el.hasClass('checked')) {
36 | scope.update();
37 | }
38 | }
39 |
40 | // check radio button, uncheck all others in name group
41 | RadioBtn.prototype.update = function() {
42 | var $span = this.$el.find('span'),
43 | // the group of radio buttons
44 | group = 'input[name="'+this.$input.attr('name')+'"]';
45 |
46 | // uncheck radio buttons in same group - uncheck input, remove checked class, remove
47 | $('.radio').has(group).removeClass('checked')
48 | .find('input').prop('checked', false).end()
49 | .find('i').remove();
50 |
51 | // check this radio button - check input, add checked class, append
52 | this.$input.prop('checked', true);
53 | $span.append('');
54 | this.$el.addClass('checked').trigger('gumby.onChange');
55 | };
56 |
57 | // add initialisation
58 | Gumby.addInitalisation('radiobtns', function() {
59 | $('.radio').each(function() {
60 | var $this = $(this);
61 | // this element has already been initialized
62 | if($this.data('isRadioBtn')) {
63 | return true;
64 | }
65 | // mark element as initialized
66 | $this.data('isRadioBtn', true);
67 | new RadioBtn($this);
68 | });
69 | });
70 |
71 | // register UI module
72 | Gumby.UIModule({
73 | module: 'radiobtn',
74 | events: ['onChange', 'check'],
75 | init: function() {
76 | Gumby.initialize('radiobtns');
77 | }
78 | });
79 | }();
80 |
--------------------------------------------------------------------------------
/public/js/libs/ui/gumby.retina.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby Retina
3 | */
4 | !function() {
5 |
6 | 'use strict';
7 |
8 | function Retina($el) {
9 | this.$el = $el;
10 | this.imageSrc = this.$el.attr('src');
11 | this.retinaSrc = this.fetchRetinaImage();
12 | this.$retinaImg = $(new Image());
13 |
14 | var scope = this;
15 |
16 | // image src not valid
17 | if(!this.retinaSrc) {
18 | return false;
19 | }
20 |
21 | // load retina image
22 | this.$retinaImg.attr('src', this.retinaSrc).load(function() {
23 | scope.retinaImageLoaded();
24 | });
25 | }
26 |
27 | // fetch retina src by appending '@2x' to image string before extension
28 | Retina.prototype.fetchRetinaImage = function() {
29 | var imgSrc = this.imageSrc,
30 | index = this.imageSrc.search(/(\.|\/)(gif|jpe?g|png)$/i);
31 |
32 | // image src is not valid
33 | if(index < 0) {
34 | return false;
35 | }
36 |
37 | // return retina src
38 | return imgSrc.substr(0, index) + '@2x' + imgSrc.substr(index, imgSrc.length);
39 | };
40 |
41 | // once retina image loaded swap original src
42 | Retina.prototype.retinaImageLoaded = function() {
43 | this.$el.attr('src', this.$retinaImg.attr('src')).trigger('gumby.onRetina');
44 | };
45 |
46 | // add initialisation
47 | Gumby.addInitalisation('retina', function() {
48 |
49 | // this module is for retina devices only
50 | if(!window.devicePixelRatio || window.devicePixelRatio <= 1) {
51 | return;
52 | }
53 |
54 | $('img[data-retina],img[gumby-retina],img[retina]').each(function() {
55 | var $this = $(this);
56 | // this element has already been initialized
57 | if($this.data('isRetina')) {
58 | return true;
59 | }
60 | // mark element as initialized
61 | $this.data('isRetina', true);
62 | new Retina($this);
63 | });
64 | });
65 |
66 | // register UI module
67 | Gumby.UIModule({
68 | module: 'retina',
69 | events: ['onRetina'],
70 | init: function() {
71 | Gumby.initialize('retina');
72 | }
73 | });
74 | }();
75 |
--------------------------------------------------------------------------------
/public/js/libs/ui/gumby.skiplink.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby SkipLink
3 | */
4 | !function() {
5 |
6 | 'use strict';
7 |
8 | function SkipLink($el) {
9 | this.$el = $el;
10 | this.targetPos = 0;
11 | this.duration = 0;
12 | this.offset = false;
13 | this.easing = '';
14 | this.update = false;
15 |
16 | // set up module based on attributes
17 | this.setup();
18 |
19 | var scope = this;
20 |
21 | // skip to target element on click or trigger of gumby.skipTo event
22 | this.$el.on(Gumby.click+' gumby.skip', function(e) {
23 |
24 | e.stopImmediatePropagation();
25 | e.preventDefault();
26 |
27 | // calculate target on each click if update var set to true
28 | if(scope.update) {
29 | scope.calculateTarget(scope.skipTo);
30 |
31 | // skip straight to target
32 | } else {
33 | scope.skipTo();
34 | }
35 | }).on('gumby.initialize', function() {
36 | scope.setup();
37 | });
38 | }
39 |
40 | // set up module based on attributes
41 | SkipLink.prototype.setup = function() {
42 | this.duration = Number(Gumby.selectAttr.apply(this.$el, ['duration'])) || 200;
43 | this.offset = Gumby.selectAttr.apply(this.$el, ['offset']) || false;
44 | this.easing = Gumby.selectAttr.apply(this.$el, ['easing']) || 'swing';
45 | this.update = Gumby.selectAttr.apply(this.$el, ['update']) ? true : false;
46 |
47 | this.calculateTarget();
48 | };
49 |
50 | // calculate target px point to skip to
51 | SkipLink.prototype.calculateTarget = function(cb) {
52 |
53 | var scope = this,
54 | target = Gumby.selectAttr.apply(this.$el, ['goto']),
55 | $target;
56 |
57 | // 'top' specified so target is 0px
58 | if(target == 'top') {
59 | this.targetPos = 0;
60 |
61 | // px point specified
62 | } else if($.isNumeric(target)) {
63 | this.targetPos = Number(target);
64 | } else {
65 |
66 | // check for element with target as selector
67 | $target = $(target);
68 |
69 | // target does not exist, we need a target
70 | if(!$target) {
71 | return false;
72 | }
73 |
74 | this.targetPos = $target.offset().top;
75 | }
76 |
77 | if(cb) {
78 | cb.apply(this);
79 | }
80 | };
81 |
82 | // animate body, html scrollTop value to target px point
83 | SkipLink.prototype.skipTo = function() {
84 | var scope = this;
85 |
86 | // slide to position of target
87 | $('html,body').animate({
88 | 'scrollTop' : this.calculateOffset()
89 | }, this.duration, this.easing).promise().done(function() {
90 | scope.$el.trigger('gumby.onComplete');
91 | });
92 | };
93 |
94 | // calculate offset with current target point
95 | SkipLink.prototype.calculateOffset = function() {
96 | // no offset so return target here
97 | if(!this.offset) {
98 | return this.targetPos;
99 | }
100 |
101 | // negative / positive
102 | var op = this.offset.substr(0, 1),
103 | off = Number(this.offset.substr(1, this.offset.length));
104 |
105 | // subtract offset from target position
106 | if(op === '-') {
107 | return this.targetPos - off;
108 | // add offset to target position
109 | } else if(op === '+') {
110 | return this.targetPos + off;
111 | }
112 | };
113 |
114 | // add initialisation
115 | Gumby.addInitalisation('skiplinks', function(all) {
116 | $('.skiplink > a, .skip').each(function() {
117 | var $this = $(this);
118 |
119 | // this element has already been initialized
120 | // and we're only initializing new modules
121 | if($this.data('isSkipLink') && !all) {
122 | return true;
123 |
124 | // this element has already been initialized
125 | // and we need to reinitialize it
126 | } else if($this.data('isSkipLink') && all) {
127 | $this.trigger('gumby.initialize');
128 | return true;
129 | }
130 |
131 | // mark element as initialized
132 | $this.data('isSkipLink', true);
133 | new SkipLink($this);
134 | });
135 | });
136 |
137 | // register UI module
138 | Gumby.UIModule({
139 | module: 'skiplink',
140 | events: ['onComplete', 'skip'],
141 | init: function() {
142 | Gumby.initialize('skiplinks');
143 | }
144 | });
145 | }();
146 |
--------------------------------------------------------------------------------
/public/js/libs/ui/gumby.tabs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby Tabs
3 | */
4 | !function() {
5 |
6 | 'use strict';
7 |
8 | function Tabs($el) {
9 |
10 | this.$el = $el;
11 | this.$nav = this.$el.find('ul.tab-nav > li');
12 | this.$content = this.$el.find('.tab-content');
13 |
14 | var scope = this;
15 |
16 | // listen for click event on tab nav and custom gumby set event
17 | this.$nav.children('a').on(Gumby.click, function(e) {
18 | e.stopImmediatePropagation();
19 | e.preventDefault();
20 | scope.click($(this));
21 | });
22 |
23 | // listen for gumby.set value for dynamically set tabs
24 | this.$el.on('gumby.set', function(e, index) {
25 | scope.set(e, index);
26 | });
27 | }
28 |
29 | // handle tab nav click event
30 | Tabs.prototype.click = function($this) {
31 | // index of item to activate
32 | var index = $this.parent().index();
33 |
34 | // deactivate other tab navigation and content
35 | this.$nav.add(this.$content).removeClass('active');
36 |
37 | // activate this tab nav link and content
38 | this.$nav.eq(index).add(this.$content.eq(index)).addClass('active');
39 |
40 | // trigger gumby.change event and pass current active tab index
41 | this.$el.trigger('gumby.onChange', index);
42 | };
43 |
44 | // set specific tab
45 | Tabs.prototype.set = function(e, index) {
46 | this.$nav.eq(index).find('a').trigger(Gumby.click);
47 | };
48 |
49 | // add initialisation
50 | Gumby.addInitalisation('tabs', function() {
51 | $('.tabs').each(function() {
52 | var $this = $(this);
53 | // this element has already been initialized
54 | if($this.data('isTabs')) {
55 | return true;
56 | }
57 | // mark element as initialized
58 | $this.data('isTabs', true);
59 | new Tabs($this);
60 | });
61 | });
62 |
63 | // register UI module
64 | Gumby.UIModule({
65 | module: 'tabs',
66 | events: ['onChange', 'set'],
67 | init: function() {
68 | Gumby.initialize('tabs');
69 | }
70 | });
71 | }();
72 |
--------------------------------------------------------------------------------
/public/js/libs/ui/jquery.validation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby jQuery Validation Plugin
3 | */
4 | !function($) {
5 |
6 | 'use strict';
7 |
8 | function Validation($this, req) {
9 |
10 | // input and holder .field
11 | this.$this = $this;
12 | this.$field = this.$this.parents('.field');
13 |
14 | // supplied validation function with default length check
15 | this.req = req || function() {
16 | return !!this.$this.val().length;
17 | };
18 |
19 | // reference to this class
20 | var scope = this;
21 |
22 | // checkboxes and radio buttons use gumby.onChange event to validate
23 | if(this.$this.is('[type=checkbox], [type=radio]')) {
24 | this.$field = this.$this.parent('label');
25 | this.$field.on('gumby.onChange', function() {
26 | scope.validate();
27 | });
28 |
29 | // selects validate on change
30 | } else if(this.$this.is('select')) {
31 | this.$field = this.$this.parents('.picker');
32 | this.$field.on('change', function() {
33 | scope.validate();
34 | });
35 |
36 | // others (text input, textarea) use blur
37 | } else {
38 | this.$this.on('blur', function(e) {
39 | // ignore tab
40 | if(e.which !== 9) {
41 | scope.validate();
42 | }
43 | });
44 | }
45 | }
46 |
47 | // validate field
48 | Validation.prototype.validate = function() {
49 |
50 | var result = this.req(this.$this);
51 |
52 | // failed
53 | if(!result) {
54 | this.$field.removeClass('success').addClass('danger');
55 |
56 | // passed
57 | } else {
58 | //} else if(this.$field.hasClass('danger')) {
59 | this.$field.removeClass('danger').addClass('success');
60 | }
61 |
62 | return result;
63 | };
64 |
65 | // jQuery plugin definition
66 | $.fn.validation = function(options) {
67 |
68 | var // extend params with defaults
69 | settings = $.extend({
70 | submit : false,
71 | fail: false,
72 | required : []
73 | }, options),
74 | // store validation objects
75 | validations = [];
76 |
77 | // init each form plugin is called on
78 | return this.each(function() {
79 |
80 | // no required fields so plugin is pointless
81 | if(!settings.required.length) {
82 | return false;
83 | }
84 |
85 | var $this = $(this),
86 | reqLength = settings.required.length,
87 | i;
88 |
89 | // loop round each required field and instantiate new validation object
90 | for(i = 0; i < reqLength; i++) {
91 | validations.push(new Validation(
92 | $this.find('[name="'+settings.required[i].name+'"]'),
93 | settings.required[i].validate || false
94 | ));
95 | }
96 |
97 | // hijack submit event
98 | $this.on('submit', function(e) {
99 |
100 | // reference to whole form pass/fail
101 | var failed = false;
102 |
103 | // if no passed attribute found we should halt form submit
104 | if(!$this.data('passed')) {
105 | e.preventDefault();
106 |
107 | // loop round validation objects and validate each
108 | var reqLength = validations.length, i;
109 | for(i = 0; i < reqLength; i++) {
110 | if(!validations[i].validate()) {
111 | failed = true;
112 | }
113 | }
114 |
115 | // passed
116 | if(!failed) {
117 | // if submit method present call that otherwise submit form
118 | if(settings.submit && typeof settings.submit === 'function') {
119 | settings.submit($this.serializeArray());
120 | return;
121 | }
122 |
123 | // store passed bool and re-submit
124 | $this.data('passed', true).submit();
125 |
126 | // failed
127 | } else {
128 | // call fail method if present
129 | if(settings.fail && typeof settings.fail === 'function') {
130 | settings.fail();
131 | return;
132 | }
133 | }
134 | }
135 | });
136 | });
137 | };
138 | }(jQuery);
139 |
--------------------------------------------------------------------------------
/public/js/main.js:
--------------------------------------------------------------------------------
1 | // Gumby is ready to go
2 | Gumby.ready(function() {
3 | console.log('Gumby is ready to go...', Gumby.debug());
4 |
5 | // placeholder polyfil
6 | if(Gumby.isOldie || Gumby.$dom.find('html').hasClass('ie9')) {
7 | $('input, textarea').placeholder();
8 | }
9 | });
10 |
11 | // Oldie document loaded
12 | Gumby.oldie(function() {
13 | console.log("This is an oldie browser...");
14 | });
15 |
16 | // Touch devices loaded
17 | Gumby.touch(function() {
18 | console.log("This is a touch enabled device...");
19 | });
20 |
21 | // Document ready
22 | $(function() {
23 | $('#skip-switch').on('gumby.onComplete', function() {
24 | $(this).trigger('gumby.trigger');
25 | });
26 | });
27 |
28 |
--------------------------------------------------------------------------------
/public/js/plugins.js:
--------------------------------------------------------------------------------
1 | window.log=function(){log.history=log.history||[];log.history.push(arguments);if(this.console){arguments.callee=arguments.callee.caller;var a=[].slice.call(arguments);(typeof console.log==="object"?log.apply.call(console.log,console,a):console.log.apply(console,a))}};
2 | (function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,timeStamp,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try
3 | {console.log();return window.console;}catch(err){return window.console={};}})());
4 |
5 | /*! http://mths.be/placeholder v2.0.7 by @mathias */
6 | ;(function(f,h,$){var a='placeholder' in h.createElement('input'),d='placeholder' in h.createElement('textarea'),i=$.fn,c=$.valHooks,k,j;if(a&&d){j=i.placeholder=function(){return this};j.input=j.textarea=true}else{j=i.placeholder=function(){var l=this;l.filter((a?'textarea':':input')+'[placeholder]').not('.placeholder').bind({'focus.placeholder':b,'blur.placeholder':e}).data('placeholder-enabled',true).trigger('blur.placeholder');return l};j.input=a;j.textarea=d;k={get:function(m){var l=$(m);return l.data('placeholder-enabled')&&l.hasClass('placeholder')?'':m.value},set:function(m,n){var l=$(m);if(!l.data('placeholder-enabled')){return m.value=n}if(n==''){m.value=n;if(m!=h.activeElement){e.call(m)}}else{if(l.hasClass('placeholder')){b.call(m,true,n)||(m.value=n)}else{m.value=n}}return l}};a||(c.input=k);d||(c.textarea=k);$(function(){$(h).delegate('form','submit.placeholder',function(){var l=$('.placeholder',this).each(b);setTimeout(function(){l.each(e)},10)})});$(f).bind('beforeunload.placeholder',function(){$('.placeholder').each(function(){this.value=''})})}function g(m){var l={},n=/^jQuery\d+$/;$.each(m.attributes,function(p,o){if(o.specified&&!n.test(o.name)){l[o.name]=o.value}});return l}function b(m,n){var l=this,o=$(l);if(l.value==o.attr('placeholder')&&o.hasClass('placeholder')){if(o.data('placeholder-password')){o=o.hide().next().show().attr('id',o.removeAttr('id').data('placeholder-id'));if(m===true){return o[0].value=n}o.focus()}else{l.value='';o.removeClass('placeholder');l==h.activeElement&&l.select()}}}function e(){var q,l=this,p=$(l),m=p,o=this.id;if(l.value==''){if(l.type=='password'){if(!p.data('placeholder-textinput')){try{q=p.clone().attr({type:'text'})}catch(n){q=$('').attr($.extend(g(this),{type:'text'}))}q.removeAttr('name').data({'placeholder-password':true,'placeholder-id':o}).bind('focus.placeholder',b);p.data({'placeholder-textinput':q,'placeholder-id':o}).before(q)}p=p.removeAttr('id').hide().prev().attr('id',o).show()}p.addClass('placeholder');p[0].value=p.attr('placeholder')}else{p.removeClass('placeholder')}}}(this,document,jQuery));
7 |
8 | // place any jQuery/helper plugins in here, instead of separate, slower script files.
9 |
--------------------------------------------------------------------------------
/public/packages/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fideloper/Implementing-Laravel/c033c07e097325554b9c220db1547ab20ec2f93d/public/packages/.gitkeep
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/public/sass/_base.scss:
--------------------------------------------------------------------------------
1 | @import "compass/typography/vertical_rhythm";
2 |
3 | @include establish-baseline;
4 |
5 | html * {
6 | @include box-sizing(border-box);
7 | }
8 |
9 | body {
10 | background: $global-bg-color;
11 | font-family: $font-family;
12 | font-weight: $body-font-weight;
13 | color: $body-font-color;
14 | position: relative;
15 | -webkit-font-smoothing: $font-smoothing;
16 | @include respond(all-phones) {
17 | -webkit-text-size-adjust: none;
18 | -ms-text-size-adjust: none;
19 | width: 100%;
20 | min-width: 0;
21 | }
22 | }
23 |
24 | html, body {
25 | height: 100%;
26 | }
27 |
28 | .ie9 {
29 | font-family: $font-family;
30 | * {
31 | font-family: $font-family;
32 | }
33 | }
34 |
35 | .hide {
36 | display: none;
37 | }
38 |
39 | .hide.active, .show {
40 | display: block;
41 | }
42 |
43 | .fixed {
44 | position: fixed;
45 | &.pinned {
46 | position: absolute;
47 | }
48 | @include respond(portrait-tablets) {
49 | position: relative !important;
50 | top: auto !important;
51 | left: auto !important;
52 | }
53 | }
54 |
55 | .unfixed {
56 | position: relative !important;
57 | top: auto !important;
58 | left: auto !important;
59 | }
60 |
61 | .text-center {
62 | text-align: center;
63 | }
64 |
65 | .text-left {
66 | text-align: left;
67 | }
68 |
69 | .text-right {
70 | text-align: right;
71 | }
72 |
--------------------------------------------------------------------------------
/public/sass/_custom.scss:
--------------------------------------------------------------------------------
1 | // Your custom SCSS should be written here...
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/sass/_fonts.scss:
--------------------------------------------------------------------------------
1 | /* Fonts */
2 |
3 | // Import Google Web Fonts
4 | @import url(//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700);
5 |
6 |
7 | // Set local icon font
8 | @font-face {
9 | font-family: '#{$icons}';
10 | font-style: normal;
11 | font-weight: 400;
12 | src: url(../fonts/icons/#{$icons}.eot);
13 | src: url('../fonts/icons/#{$icons}.eot?#iefix') format('ie9-skip-eot'),
14 | url('../fonts/icons/#{$icons}.woff') format('woff'),
15 | url('../fonts/icons/#{$icons}.ttf') format('truetype');
16 | }
17 |
18 | // To include your own, local copies of fonts, use the following template
19 | //
20 | //@font-face {
21 | // font-family: '#{$some-font-variable}';
22 | // font-style: normal;
23 | // font-weight: 400;
24 | // src: url(../fonts/icons/#{$some-font-variable}.eot);
25 | // src: url('../fonts/icons/#{$some-font-variable}.eot?#iefix') format('ie9-skip-eot'),
26 | // url('../fonts/icons/#{$some-font-variable}.woff') format('woff'),
27 | // url('../fonts/icons/#{$some-font-variable}.ttf') format('truetype');
28 | //}
29 |
--------------------------------------------------------------------------------
/public/sass/extensions/modular-scale/lib/modular-scale.rb:
--------------------------------------------------------------------------------
1 | require 'compass'
2 | Compass::Frameworks.register('sassy-math', :path => File.expand_path("../.."))
3 |
4 |
5 | # This tells Compass what your Compass extension is called, and where to find
6 | # its files
7 | # Replace 'extension' with the name of your extension. Spaces allowed.
8 | extension_path = File.expand_path(File.join(File.dirname(__FILE__), ".."))
9 | Compass::Frameworks.register('modular-scale', :path => extension_path)
10 |
11 | # Version and date of version for your Compass extension.
12 | # Replace Extension with the name of your extension
13 | # Letters, numbers, and underscores only
14 | # Version is a number. If a version contains alphas, it will be created as
15 | # a prerelease version
16 | # Date is in the form of YYYY-MM-DD
17 | module ModularScale
18 | VERSION = "1.0.6"
19 | DATE = "2012-08-13"
20 | end
21 |
22 | # This is where any custom SassScript should be placed. The functions will be
23 | # available on require of your extension without the need for users to import
24 | # any partials. Uncomment below.
25 |
26 | # Modular Scale Sass Script
27 | module Sass::Script
28 | class Number < Literal
29 | # Comparison
30 | def <=>(other)
31 | value <=> other.value
32 | end
33 | end
34 | end
35 |
36 | module Sass::Script::Functions
37 | # Modular Scale
38 | def double_octave
39 | value = 4 / 1.0
40 | Sass::Script::Number.new(value)
41 | end
42 | def major_twelfth
43 | value = 3 / 1.0
44 | Sass::Script::Number.new(value)
45 | end
46 | def major_eleventh
47 | value = 8 / 3.0
48 | Sass::Script::Number.new(value)
49 | end
50 | def major_tenth
51 | value = 5 / 2.0
52 | Sass::Script::Number.new(value)
53 | end
54 | def octave
55 | value = 2 / 1.0
56 | Sass::Script::Number.new(value)
57 | end
58 | def major_seventh
59 | value = 15 / 8.0
60 | Sass::Script::Number.new(value)
61 | end
62 | def minor_seventh
63 | value = 16 /9.0
64 | Sass::Script::Number.new(value)
65 | end
66 | def major_sixth
67 | value = 5 / 3.0
68 | Sass::Script::Number.new(value)
69 | end
70 | def minor_sixth
71 | value = 8 / 5.0
72 | Sass::Script::Number.new(value)
73 | end
74 | def fifth
75 | value = 3 / 2.0
76 | Sass::Script::Number.new(value)
77 | end
78 | def augmented_fourth
79 | value = Math.sqrt(2) / 1.0
80 | Sass::Script::Number.new(value)
81 | end
82 | def fourth
83 | value = 4 / 3.0
84 | Sass::Script::Number.new(value)
85 | end
86 | def major_third
87 | value = 5 / 4.0
88 | Sass::Script::Number.new(value)
89 | end
90 | def minor_third
91 | value = 6 / 5.0
92 | Sass::Script::Number.new(value)
93 | end
94 | def major_second
95 | value = 9 / 8.0
96 | Sass::Script::Number.new(value)
97 | end
98 | def minor_second
99 | value = 16 / 15.0
100 | Sass::Script::Number.new(value)
101 | end
102 |
103 | # Lists
104 | def sort_list(list)
105 | sep = list.separator if list.is_a?(Sass::Script::List)
106 | list = list.to_a.sort
107 | Sass::Script::List.new(list, sep)
108 | end
109 | def reverse_list(list)
110 | sep = list.separator if list.is_a?(Sass::Script::List)
111 | list = list.to_a.reverse
112 | Sass::Script::List.new(list, sep)
113 | end
114 | def trim_list(list, threshold, ascending)
115 | # remove list items above or below a threshold
116 | sep = list.separator if list.is_a?(Sass::Script::List)
117 | list = list.to_a
118 | if ascending.value
119 | list = list.delete_if {
120 | |x| x.value <= threshold.value
121 | }
122 | else
123 | list = list.delete_if {
124 | |x| x.value >= threshold.value
125 | }
126 | end
127 | Sass::Script::List.new(list, sep)
128 | end
129 | end
130 |
--------------------------------------------------------------------------------
/public/sass/extensions/sassy-math/lib/sassy-math.rb:
--------------------------------------------------------------------------------
1 | require 'compass'
2 | Compass::Frameworks.register("sassy-math", :path => "#{File.dirname(__FILE__)}/..")
3 |
4 | # Sassy math Functions
5 | module Sass::Script::Functions
6 | # Exponents
7 | def exponent(base, powerNum, powerDen)
8 | base = base.value.to_f
9 | powerNum = powerNum.value.to_f
10 | powerDen = powerDen.value.to_f
11 | result = base ** (powerNum / powerDen)
12 | Sass::Script::Number.new(result)
13 | end
14 | def power(base, exponent)
15 | base = base.value.to_f
16 | exponent = exponent.value.to_f
17 | result = base ** exponent
18 | Sass::Script::Number.new(result)
19 | end
20 | def sqrt(number)
21 | number = number.value.to_f
22 | result = Math.sqrt(number)
23 | Sass::Script::Number.new(result)
24 | end
25 | def nth_root(number, root)
26 | number = number.value.to_f
27 | root = root.value.to_f
28 | result = number ** (1.0 / root)
29 | Sass::Script::Number.new(result)
30 | end
31 | # Logarithms
32 | def ln(num)
33 | result = Math.log(num.value)
34 | Sass::Script::Number.new(result)
35 | end
36 | def log10(num)
37 | result = Math.log10(num.value)
38 | Sass::Script::Number.new(result)
39 | end
40 | # Miscellaneous
41 | def factorial(number)
42 | result = 1
43 | number = number.value
44 | if number > 0
45 | (1..number).each do |i|
46 | result = result * i
47 | end
48 | end
49 | Sass::Script::Number.new(result)
50 | end
51 | def random(max = Sass::Script::Number.new(100)) ## shamelessly taken from here: https://gist.github.com/1561650
52 | Sass::Script::Number.new(rand(max.value), max.numerator_units, max.denominator_units)
53 | end
54 | def hypot(a, b)
55 | a = a.value.to_f
56 | b = b.value.to_f
57 | result = Math.hypot(a, b)
58 | Sass::Script::Number.new(result)
59 | end
60 | # Constants
61 | def pi
62 | pi = Math::PI
63 | Sass::Script::Number.new(pi)
64 | end
65 | def e
66 | e = Math::E
67 | Sass::Script::Number.new(e)
68 | end
69 | def golden_ratio()
70 | result = (1.0 / 2.0) + (Math.sqrt(5) / 2.0)
71 | Sass::Script::Number.new(result)
72 | end
73 | # Comparative Functions
74 | def is_int(number)
75 | number = number.value.to_f
76 | if number - number.floor != 0
77 | result = false
78 | else
79 | result = true
80 | end
81 | Sass::Script::Bool.new(result)
82 | end
83 | def is_float(number)
84 | number = number.value
85 | if number - number.floor != 0
86 | result = true
87 | else
88 | result = false
89 | end
90 | Sass::Script::Bool.new(result)
91 | end
92 | # Trigonometric Functions
93 | def deg_to_rad(degree)
94 | result = degree.value.to_f * Math::PI / 180
95 | Sass::Script::Number.new(result)
96 | end
97 | def rad_to_deg(rad)
98 | result = rad.value.to_f * 180 / Math::PI
99 | Sass::Script::Number.new(result)
100 | end
101 | def cosh(rad)
102 | rad = rad.value.to_f
103 | result = Math.cosh(rad)
104 | Sass::Script::Number.new(result)
105 | end
106 | def acos(rad)
107 | rad = rad.value.to_f
108 | result = Math.acos(rad)
109 | Sass::Script::Number.new(result)
110 | end
111 | def acosh(rad)
112 | rad = rad.value.to_f
113 | result = Math.acosh(rad)
114 | Sass::Script::Number.new(result)
115 | end
116 | def sinh(rad)
117 | rad = rad.value.to_f
118 | result = Math.sinh(rad)
119 | Sass::Script::Number.new(result)
120 | end
121 | def asin(rad)
122 | rad = rad.value.to_f
123 | result = Math.asin(rad)
124 | Sass::Script::Number.new(result)
125 | end
126 | def asinh(rad)
127 | rad = rad.value.to_f
128 | result = Math.asinh(rad)
129 | Sass::Script::Number.new(result)
130 | end
131 | def tanh(rad)
132 | rad = rad.value.to_f
133 | result = Math.tanh(rad)
134 | Sass::Script::Number.new(result)
135 | end
136 | def atan(rad)
137 | rad = rad.value.to_f
138 | result = Math.atan(rad)
139 | Sass::Script::Number.new(result)
140 | end
141 | def atan2(y, x)
142 | y = y.value.to_f
143 | x = x.value.to_f
144 | result = Math.atan2(y, x)
145 | Sass::Script::Number.new(result)
146 | end
147 | def atanh(rad)
148 | rad = rad.value.to_f
149 | result = Math.atanh(rad)
150 | Sass::Script::Number.new(result)
151 | end
152 | end
153 |
154 | module SassyMath
155 |
156 | VERSION = "1.5"
157 | DATE = "2012-07-29"
158 |
159 | end
--------------------------------------------------------------------------------
/public/sass/functions/_all.scss:
--------------------------------------------------------------------------------
1 | // Global Gumby Functions
2 |
3 | @import "breakpoints";
4 | @import "strip-units";
5 | @import "grid-calc";
6 |
7 | // Global Gumby Mixins
8 |
9 | @import "clearfix";
10 | @import "typography";
11 | @import "responsivity";
12 | @import "line-and-height";
13 | @import "height-calc";
14 | @import "semantic-grid";
15 | @import "visibility";
16 |
--------------------------------------------------------------------------------
/public/sass/functions/_breakpoints.scss:
--------------------------------------------------------------------------------
1 | @function breakpoint($breakpoint) {
2 | @if $breakpoint == $document-width {
3 | @return $document-width - 1;
4 | }
5 | @if $breakpoint == $tablet-device-width {
6 | @return $tablet-device-width - 1;
7 | }
8 | @if $breakpoint == $min-device-width {
9 | @return $min-device-width + 1;
10 | }
11 | }
--------------------------------------------------------------------------------
/public/sass/functions/_buttons.scss:
--------------------------------------------------------------------------------
1 | @mixin button-size($size) {
2 | $n: 0;
3 | @if $size == xlarge {
4 | $n: $xlarge-button-font-size;
5 | }
6 | @if $size == large {
7 | $n: $large-button-font-size;
8 | }
9 | @if $size == medium {
10 | $n: $medium-button-font-size;
11 | }
12 | @if $size == small {
13 | $n: $small-button-font-size;
14 | }
15 | $button-font-size: $n;
16 | $button-height: ms($ratio, $button-font-size) + 1;
17 | $line-height: $button-height - 2;
18 |
19 | @include font-size($button-font-size);
20 | @include line-and-height($button-height);
21 |
22 | a {
23 | position:relative;
24 | padding: 0 ms(0, $button-font-size);
25 | }
26 |
27 | &.icon-left {
28 | a {
29 | padding-left: $button-height;
30 | &:before {
31 | left: $button-font-size / 1.5;
32 | }
33 | }
34 | }
35 |
36 | &.icon-right {
37 | a {
38 | padding-right: $button-height;
39 | &:after {
40 | right: $button-font-size / 1.5;
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/sass/functions/_clearfix.scss:
--------------------------------------------------------------------------------
1 | @mixin clearfix() {
2 | *zoom:1;
3 | &:before, &:after {
4 | content: "";
5 | display: table;
6 | }
7 | &:after {
8 | clear: both;
9 | }
10 | }
11 |
12 | @mixin mobilefix() {
13 | @include respond(all-phones) {
14 | &:before, &:after {
15 | content: " ";
16 | display: table;
17 | }
18 | &:after {
19 | clear: both;
20 | }
21 | &:last-child {
22 | float: none;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/public/sass/functions/_forms.scss:
--------------------------------------------------------------------------------
1 | @mixin input-size($size) {
2 | @if $size == xxwide { $size: 100%; }
3 | @if $size == xwide { $size: 82.6666666667%; }
4 | @if $size == wide { $size: 65.3333333333%; }
5 | @if $size == normal { $size: 48%; }
6 | @if $size == narrow { $size: 30.6666666667%; }
7 | @if $size == xnarrow { $size: 13.3333333333%; }
8 |
9 | width: $size;
10 | }
11 |
12 | @mixin input-sizes-list() {
13 | $sizes: ();
14 | @each $item in $field-sizes {
15 | $sizes: join($sizes, unquote(".#{$item} "), comma);
16 | }
17 | #{$sizes} { @content }
18 | }
19 |
--------------------------------------------------------------------------------
/public/sass/functions/_grid-calc.scss:
--------------------------------------------------------------------------------
1 | // Calculate grid values
2 | $gutter: percentage($gutter-in-px / $row-max-width); // 2.1276596
3 |
4 | // Return single column width
5 | @function oneCol($hybrid-grid: false) {
6 | @if ($hybrid-grid == true){
7 | @return (100% - ($gutter * ($hybrid - 1))) / $hybrid;
8 | }
9 | @else{
10 | @return (100% - ($gutter * ($cols - 1))) / $cols;
11 | }
12 | }
13 |
14 | // Calculate Grid Column Widths
15 | @function columns($num, $hybrid-grid: false){
16 | @if ($hybrid-grid == true) {
17 | @return (oneCol(true) * $num) + ($gutter * ($num - 1));
18 | }
19 | @else {
20 | @return (oneCol() * $num) + ($gutter * ($num - 1)); // (One column * 'x') + (gutter * ('x' - 1)) = Column Width
21 | }
22 | }
23 |
24 |
25 | // Calculate Push Class Margins
26 | @function push_x($num, $first-child: false, $is-hybrid: false) {
27 | @if $first-child and $is-hybrid {
28 | @return (oneCol(true) * $num) + ($gutter * ($num - 1)) + $gutter; // Column width + gutter
29 | }
30 | @else if $first-child != true and $is_hybrid{
31 | @return (oneCol(true) * $num) + ($gutter * ($num - 1)) + ($gutter * 2); // Column width + (gutter * 2)
32 | }
33 | @else if $first-child and $is_hybrid != true{
34 | @return (oneCol() * $num) + ($gutter * ($num - 1)) + $gutter;
35 | }
36 | @else {
37 | @return (oneCol() * $num) + ($gutter * ($num - 1)) + ($gutter * 2); // Column width + (gutter * 2)
38 | }
39 | }
40 |
41 | // Calculate Centered Class Margins
42 | @function centered($num, $hybrid-grid: false) {
43 | @if $hybrid-grid{
44 | @return 50% - ((($num * (oneCol(true))) + (($num - 1) * $gutter)) / 2);
45 | }
46 | @else{
47 | @return 50% - ((($num * (oneCol())) + (($num - 1) * $gutter)) / 2);
48 | }
49 | }
50 |
51 | // Create class names from column count integers
52 | @function number-as-word($number){
53 | $w: "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven",
54 | "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen",
55 | "twenty", "twenty-one", "twenty-two", "twenty-three", "twenty-four", "twenty-five", "twenty-six", "twenty-seven",
56 | "twenty-eight", "twenty-nine", "thirty", "thirty-one", "thirty-two", "thirty-three",
57 | "thirty-four", "thirty-five", "thirty-six";
58 | @return nth($w, $number);
59 | }
60 |
--------------------------------------------------------------------------------
/public/sass/functions/_height-calc.scss:
--------------------------------------------------------------------------------
1 |
2 | // Calculate the height of an object based on its scale
3 |
4 | @function height-calc($size) {
5 | @return ms($ratio, $size) + 1;
6 | }
7 |
--------------------------------------------------------------------------------
/public/sass/functions/_line-and-height.scss:
--------------------------------------------------------------------------------
1 |
2 | // Make line-height equal to an element's height
3 |
4 | @mixin line-and-height($height) {
5 | height: $height;
6 | line-height: $height - 2;
7 | }
8 |
--------------------------------------------------------------------------------
/public/sass/functions/_responsivity.scss:
--------------------------------------------------------------------------------
1 | // Responsive Mixins
2 |
3 | @mixin respond($media) {
4 | @if $media == portrait-phones {
5 | @media only screen and (max-width: $min-device-width) { @content; }
6 | }
7 | @else if $media == landscape-phones {
8 | @media only screen and (min-width: breakpoint($min-device-width)) and (max-width: breakpoint($tablet-device-width)) { @content; }
9 | }
10 | @else if $media == all-phones {
11 | @media only screen and (max-width: breakpoint($tablet-device-width)) { @content; }
12 | }
13 | @else if $media == portrait-tablets {
14 | @media only screen and (max-width: $tablet-device-width) { @content; }
15 | }
16 | @else if $media == tablets {
17 | @media only screen and (min-width: $tablet-device-width) and (max-width: $document-width - 1) { @content; }
18 | }
19 | @else if $media == desktop {
20 | @media only screen and (min-width: $tablet-device-width) { @content; }
21 | }
22 | @else if $media == document-width {
23 | @media only screen and (max-width: $document-width + 20) { @content; }
24 | }
25 | @else if $media == large-screens {
26 | @media only screen and (min-width: $max-device-width) { @content; }
27 | }
28 | @else if $media == print {
29 | @media print { @content; }
30 | }
31 | @else {
32 | @media only screen and ($media) { @content; }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/public/sass/functions/_semantic-grid.scss:
--------------------------------------------------------------------------------
1 |
2 |
3 | // Gumby Semantic Grid Mixin //
4 |
5 |
6 | // Mixin for rows
7 |
8 | @mixin row($nested: false) {
9 | @if $nested == nested {
10 | width: auto;
11 | min-width: 0px;
12 | max-width: none;
13 | @extend %clearfix;
14 | }
15 | @else {
16 | width: 100%;
17 | max-width: $row-max-width;
18 | min-width: $min-device-width;
19 | margin: 0 auto;
20 | @extend %clearfix;
21 | }
22 | > *:first-child {
23 | margin-left: 0px;
24 | }
25 | @include respond(document-width) {
26 | padding: 0 20px;
27 | }
28 | @include respond(all-phones) {
29 | width: auto;
30 | min-width: 0;
31 | margin-left: 0;
32 | margin-right: 0;
33 | }
34 | }
35 |
36 | // Mixin for rows that are nested within columns
37 |
38 | @mixin nestedRow() {
39 | width: auto;
40 | min-width: 0px;
41 | max-width: none;
42 | @extend %clearfix;
43 | }
44 |
45 |
46 | @mixin column($columns:$columns, $alignment: false, $behavior: false) {
47 | @if $alignment == center {
48 | @extend %columnconfig;
49 | float: none;
50 | margin-left: auto !important;
51 | margin-right: auto !important;
52 | width: columns($columns);
53 | @include respond(all-phones) {
54 | float: left;
55 | margin-left: 0;
56 | width: 100%;
57 | }
58 | }
59 | @else if $behavior == collapse {
60 | @extend %columnconfig;
61 | @extend %collapse;
62 | width: columns($columns);
63 | @include respond(all-phones) {
64 | float: left;
65 | width: 100%;
66 | }
67 | }
68 | @else {
69 | @extend %columnconfig;
70 | width: columns($columns);
71 | @include respond(all-phones) {
72 | float: left;
73 | margin-left: 0;
74 | width: 100%;
75 | }
76 | }
77 | }
78 |
79 | @mixin hybrid($columns:$columns, $alignment: false, $behavior: false) {
80 | @if $alignment == center {
81 | @extend %columnconfig;
82 | float: none;
83 | margin-left: auto !important;
84 | margin-right: auto !important;
85 | width: columns($columns, true);
86 | @include respond(all-phones) {
87 | float: left;
88 | margin-left: 0;
89 | width: 100%;
90 | }
91 | }
92 | @else if $behavior == collapse {
93 | @extend %columnconfig;
94 | @extend %collapse;
95 | width: columns($columns, true);
96 | @include respond(all-phones) {
97 | float: left;
98 | width: 100%;
99 | }
100 | }
101 | @else {
102 | @extend %columnconfig;
103 | width: columns($columns, true);
104 | @include respond(all-phones) {
105 | float: left;
106 | margin-left: 0;
107 | width: 100%;
108 | }
109 | }
110 | }
111 |
112 | @mixin push($columns, $hybrid-grid: false) {
113 | @if $hybrid-grid == hybrid {
114 | margin-left: push_x($columns, false, true);
115 | &:first-child {
116 | margin-left: push_x($columns, true, true);
117 | }
118 | @include respond(all-phones) {
119 | margin-left: 0;
120 | &:first-child {
121 | margin-left: 0;
122 | }
123 | }
124 | }
125 | @else {
126 | margin-left: push_x($columns);
127 | &:first-child {
128 | margin-left: push_x($columns, true);
129 | }
130 | @include respond(all-phones) {
131 | margin-left: 0;
132 | &:first-child {
133 | margin-left: 0;
134 | }
135 | }
136 | }
137 | }
138 |
139 | @mixin pull($direction:false) {
140 | @if $direction == left {
141 | @extend %pull-left;
142 | }
143 | @elseif $direction == none {
144 | @extend %pull-none;
145 | }
146 | @else {
147 | @extend %pull-right;
148 | }
149 | }
150 |
151 |
152 | // Placeholders for the Semantic Grid
153 |
154 | %container {
155 | padding: 0px $gutter-in-px + px;
156 | @include respond(all-phones) {
157 | min-width: 0;
158 | margin-left: 0;
159 | margin-right: 0;
160 | }
161 | }
162 |
163 | // Clearfix placeholder
164 | %clearfix { @include clearfix(); }
165 |
166 | // Clearfix placeholder for mobile
167 | %mobilefix { @include mobilefix(); }
168 |
169 | // Row placeholders
170 | %row { @include row(); }
171 | %nestedrow { @include row(); }
172 |
173 | // Column Configuration placeholder
174 | %columnconfig {
175 | margin-left: $gutter;
176 | float: $default-float;
177 | min-height: 1px;
178 | position: relative;
179 | @include box-sizing(border-box);
180 | }
181 |
182 | %pull-right { float: right; }
183 | %pull-left { float: left; }
184 | %pull-none { float: none; }
185 |
186 | // Collapse Gutters
187 | %collapse {
188 | margin-left: 0px;
189 | }
190 |
--------------------------------------------------------------------------------
/public/sass/functions/_strip-units.scss:
--------------------------------------------------------------------------------
1 |
2 | // Strip out units to do math functions.
3 | @function strip-units($number) {
4 | @return $number / ($number * 0 + 1);
5 | }
6 |
--------------------------------------------------------------------------------
/public/sass/functions/_typography.scss:
--------------------------------------------------------------------------------
1 | // Typography mixins
2 |
3 | // Fonts in rems with px fallback
4 |
5 | @mixin font-size($size, $is-important: false) {
6 | $size: if(unitless($size), $size, $size / 1px);
7 |
8 | @if $is-important {
9 | font-size: $size + px !important;
10 | font-size: ($size / strip-units($base-font-size)) + rem !important;
11 | } @else {
12 | font-size: $size + px;
13 | font-size: ($size / strip-units($base-font-size)) + rem;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/public/sass/functions/_visibility.scss:
--------------------------------------------------------------------------------
1 | // Visibility Mixins
2 |
3 | // Mixin for hidden
4 |
5 | @mixin hidden($media) {
6 | @include respond($media) {
7 | display: none !important;
8 | }
9 | }
10 |
11 | // Mixin for visible
12 |
13 | @mixin visible($media) {
14 | @include respond($media) {
15 | display: inherit !important;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/public/sass/gumby.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Gumby Framework
3 | * ---------------
4 | *
5 | * Follow @gumbycss on twitter and spread the love.
6 | * We worked super hard on making this awesome and released it to the web.
7 | * All we ask is you leave this intact. #gumbyisawesome
8 | *
9 | * Gumby Framework
10 | * http://gumbyframework.com
11 | *
12 | * Built with love by your friends @digitalsurgeons
13 | * http://www.digitalsurgeons.com
14 | *
15 | * Free to use under the MIT license.
16 | * http://www.opensource.org/licenses/mit-license.php
17 | */
18 |
19 | @charset "UTF-8";
20 |
21 | @import "modular-scale";
22 |
23 | @import "var/settings";
24 | @import "var/lists";
25 |
26 | @import "compass";
27 | @import "compass/reset";
28 |
29 | @import "functions/all";
30 |
31 | @import "base";
32 | @import "fonts";
33 | @import "typography";
34 | @import "grid";
35 | @import "ui/all";
36 |
37 | @import "custom";
--------------------------------------------------------------------------------
/public/sass/ui/_all.scss:
--------------------------------------------------------------------------------
1 | @import "navbar";
2 | @import "buttons";
3 | @import "icons";
4 | @import "forms";
5 | @import "labels";
6 | @import "tabs";
7 | @import "images";
8 | @import "video";
9 | @import "toggles";
10 | @import "tables";
11 |
--------------------------------------------------------------------------------
/public/sass/ui/_buttons.scss:
--------------------------------------------------------------------------------
1 | /* Buttons */
2 |
3 | // Buttons
4 | @import "../functions/buttons";
5 |
6 | .btn, .skiplink {
7 | display: inline-block;
8 | width: auto;
9 | background: $default-color;
10 | -webkit-appearance: none;
11 | font-family: $font-family;
12 | font-weight: $button-font-weight;
13 | padding: 0 !important;
14 | text-align: center;
15 | .pretty & { @extend .pretty.btn; }
16 | .metro & { @extend .metro.btn; }
17 |
18 | > a, input, button {
19 | display: block;
20 | padding: 0 $default-button-padding;
21 | color: $white;
22 | height: 100%;
23 | }
24 |
25 | input, button {
26 | background: none;
27 | border: none;
28 | width: 100%;
29 | font-size: 100%;
30 | cursor: pointer;
31 | font-weight: $type-font-weight;
32 | @include appearance(none);
33 | }
34 |
35 | &.xlarge {
36 | @include button-size(xlarge);
37 | }
38 | &.large {
39 | @include button-size(large);
40 | }
41 | &.medium {
42 | @include button-size(medium);
43 | a {
44 | padding: 0 ms(1);
45 | }
46 | }
47 | &.small {
48 | @include button-size(small);
49 | a {
50 | padding: 0 ms(-1);
51 | }
52 | }
53 |
54 | &.oval {
55 | @include border-radius(1000px);
56 | }
57 |
58 | &.pill-left {
59 | @include border-radius(500px 0 0 500px);
60 | }
61 |
62 | &.pill-right {
63 | @include border-radius(0 500px 500px 0);
64 | }
65 |
66 | @each $shade in $ui-coloring {
67 | &.#{nth($shade, 1)} {
68 | background: nth($shade, 2);
69 | border: 1px solid nth($shade, 2);
70 | &:hover {
71 | background: lighten(nth($shade, 2), 10%);
72 | }
73 | &:active {
74 | background: darken(nth($shade, 2), 10%);
75 | }
76 | @if nth($shade, 1) == default {
77 | color: darken(nth($shade, 2), 61.5%);
78 | border: 1px solid nth($shade, 2);
79 | &:hover {
80 | border: 1px solid darken(nth($shade, 2), 5%);
81 | }
82 | a, input, button {
83 | color: darken(nth($shade, 2), 61.5%);
84 | }
85 | }
86 | @if nth($shade, 1) == warning {
87 | color: darken(nth($shade, 2), 40%);
88 | a, input, button {
89 | color: darken(nth($shade, 2), 40%);
90 | }
91 | }
92 | }
93 | }
94 |
95 | @each $style in $styling {
96 | &.#{nth($style, 1)} {
97 | @include border-radius(nth($style, 2));
98 | &:hover {
99 | @extend .btn.#{nth($style, 1)}
100 | }
101 | &:active {
102 | @extend .btn.#{nth($style, 1)}
103 | }
104 | @if nth($style, 1) == metro {
105 | &.rounded {
106 | @include border-radius($button-radius);
107 | }
108 | }
109 | @if nth($style, 1) == pretty {
110 | &.squared {
111 | @include border-radius($metro-radius);
112 | }
113 | }
114 | }
115 | }
116 |
117 | &.pretty {
118 | @each $grade in $ui-coloring {
119 | &.#{nth($grade, 1)} {
120 | @include background-image(linear-gradient(lighten(nth($grade, 2), 20%), saturate(nth($grade, 2), 5%)));
121 | @include box-shadow(inset 0 0 3px lighten(nth($grade, 2), 45%));
122 | border: 1px solid darken(nth($grade, 2), 15%);
123 | &:hover {
124 | @include background-image(linear-gradient(lighten(nth($grade, 3), 15%), saturate(nth($grade, 3), 5%)));
125 | @include box-shadow(inset 0 0 3px lighten(nth($grade, 3), 40%));
126 | border: 1px solid darken(nth($grade, 3), 15%);
127 | }
128 | &:active {
129 | @include background-image(linear-gradient(saturate(nth($grade, 2), 5%), lighten(nth($grade, 2), 20%)));
130 | @include box-shadow(inset 0 0 3px lighten(nth($grade, 2), 50%));
131 | }
132 | @if nth($grade, 1) == default {
133 | a, input, button {
134 | text-shadow: 0 1px 1px lighten(nth($grade, 2), 20%);
135 | }
136 | }
137 | @elseif nth($grade, 1) == warning {
138 | color: darken(nth($grade, 2), 40%);
139 | a, input, button {
140 | text-shadow: 0 1px 1px lighten(nth($grade, 2), 20%);
141 | }
142 | }
143 | @else {
144 | a, input, button {
145 | text-shadow: 0 1px 1px darken(nth($grade, 2), 20%);
146 | }
147 | }
148 | }
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/public/sass/ui/_icons.scss:
--------------------------------------------------------------------------------
1 | /* Icons */
2 |
3 | @import "../var/icons/entypo";
4 |
5 | [class^="icon-"] a:before,
6 | [class*=" icon-"] a:before,
7 | [class^="icon-"] a:after,
8 | [class*=" icon-"] a:after,
9 | i[class^="icon-"],
10 | i[class*=" icon-"] {
11 | font-family: "#{$icons}";
12 | position:absolute;
13 | text-decoration:none;
14 | zoom: 1;
15 | }
16 |
17 | i[class^="icon-"],
18 | i[class*=" icon-"] {
19 | display: inline-block;
20 | position: static;
21 | min-width: 20px;
22 | margin: 0 5px;
23 | text-align: center;
24 | }
25 |
26 | @each $icon in $entypo-icons {
27 | .#{nth($icon, 1)} {
28 | &.icon-left a:before, &.icon-right a:after {
29 | content: "#{nth($icon, 2)}";
30 | height: inherit;
31 | }
32 | }
33 | i.#{nth($icon, 1)}:before {
34 | content: "#{nth($icon, 2)}";
35 | height: inherit;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/public/sass/ui/_images.scss:
--------------------------------------------------------------------------------
1 | .image {
2 | line-height:0;
3 | margin-bottom: 20px;
4 | &.circle {
5 | @include border-radius(50% !important);
6 | overflow: hidden;
7 | width: auto;
8 | }
9 | &.rounded {
10 | overflow: hidden;
11 | @include border-radius($button-radius $button-radius);
12 | }
13 | &.photo {
14 | border: 5px solid #fff;
15 | @include box-shadow(0 0 1px $body-font-color);
16 | &.polaroid {
17 | padding-bottom: 50px;
18 | background: #fff;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/public/sass/ui/_labels.scss:
--------------------------------------------------------------------------------
1 | /* Labels */
2 |
3 | .badge, .label {
4 | height: 20px;
5 | display: inline-block;
6 | font-family: Helvetica, arial, verdana, sans-serif;
7 | font-weight: bold;
8 | line-height: 20px;
9 | text-align:center;
10 | color: #fff;
11 | a {
12 | color: #fff;
13 | }
14 | @each $shade in $ui-coloring {
15 | &.#{nth($shade, 1)} {
16 | background: nth($shade, 2);
17 | @if nth($shade, 1) == default {
18 | color: darken(nth($shade, 2), 61.5%);
19 | border: 1px solid nth($shade, 2);
20 | &:hover {
21 | border: 1px solid darken(nth($shade, 2), 5%);
22 | }
23 | a {
24 | color: darken(nth($shade, 2), 61.5%);
25 | }
26 | }
27 | @if nth($shade, 1) == warning {
28 | color: darken(nth($shade, 2), 40%);
29 | a {
30 | color: darken(nth($shade, 2), 40%);
31 | }
32 | }
33 | }
34 | &.light {
35 | background: #fff;
36 | color: $body-font-color;
37 | border: 1px solid $default-color;
38 | a {
39 | color: $body-link-color;
40 | }
41 | }
42 | &.dark {
43 | background: #212121;
44 | }
45 | }
46 | }
47 |
48 | .badge {
49 | padding: 0 10px;
50 | @include font-size(ms(0, 14px));
51 | @include border-radius(10px);
52 | }
53 |
54 | .label {
55 | padding: 0 10px;
56 | @include font-size(ms(0, 12px));
57 | @include border-radius(2px);
58 | }
59 |
60 | .alert {
61 | padding: 0 10px;
62 | font-family: $font-family;
63 | font-weight: $font-weight-semibold;
64 | list-style-type: none;
65 | word-wrap: break-word;
66 | margin-bottom: $norm / 2;
67 | @include font-size(ms(0, 14px));
68 | @include border-radius($button-radius);
69 | @each $shade in $ui-coloring {
70 | &.#{nth($shade, 1)} {
71 | background: lighten(nth($shade, 2), 20%);
72 | border: 1px solid nth($shade, 2);
73 | color: darken(nth($shade, 2), 20%);
74 | @if nth($shade, 1) == info {
75 | color: $default-color;
76 | }
77 | @if nth($shade, 1) == default {
78 | color: darken(nth($shade, 2), 61.5%);
79 | border: 1px solid nth($shade, 2);
80 | }
81 | @if nth($shade, 1) == warning {
82 | color: darken(nth($shade, 2), 40%);
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/public/sass/ui/_tables.scss:
--------------------------------------------------------------------------------
1 | table {
2 | display: table;
3 | background-color: $table-bgcolor;
4 | border-collapse: collapse;
5 | border-spacing: 0;
6 | margin-bottom: 20px;
7 | width: 100%;
8 | border: $table-border-size $table-border-style $table-border-color;
9 |
10 | caption {
11 | text-align: center;
12 | font-size: $larger;
13 | padding: .75em;
14 | }
15 |
16 | thead th,
17 | tbody td,
18 | tr td {
19 | display: table-cell;
20 | padding: 10px;
21 | vertical-align: top;
22 | text-align: left;
23 | border-top: $table-cell-border-size $table-cell-border-style $table-cell-border-color;
24 | }
25 |
26 | tr td, tbody tr td {
27 | font-size: $norm;
28 | }
29 |
30 | tr td:first-child {
31 | font-weight: $table-row-first-cell-font-weight;
32 | }
33 |
34 | thead {
35 | background-color: $table-thead-bgcolor;
36 | color: #fff;
37 |
38 | tr th {
39 | font-size: $norm;
40 | font-weight: bold;
41 | vertical-align: bottom;
42 | }
43 | }
44 |
45 | &.striped tr:nth-of-type(even),
46 | table tr.stripe,
47 | table tr.striped {
48 | background-color: $table-stripe-bgcolor;
49 | }
50 |
51 | &.rounded {
52 | border-radius: $table-border-radius;
53 | border-collapse: separate;
54 |
55 | caption + thead tr:first-child th:first-child,
56 | caption + tr td:first-child,
57 | > thead tr:first-child th:first-child,
58 | > thead tr:first-child td:first-child,
59 | > tr:first-child td:first-child {
60 | border-top-left-radius: $table-border-radius;
61 | }
62 |
63 | caption + thead tr:first-child th:last-child,
64 | caption + tr td:last-child,
65 | > thead tr:first-child th:last-child,
66 | > thead tr:first-child td:last-child,
67 | > tr:first-child td:last-child {
68 | border-top-right-radius: $table-border-radius;
69 | }
70 |
71 | thead ~ tr:last-child td:last-child,
72 | tbody tr:last-child td:last-child {
73 | border-bottom-right-radius: $table-border-radius;
74 | }
75 |
76 | thead ~ tr:last-child td:first-child,
77 | tbody tr:last-child td:first-child {
78 | border-bottom-left-radius: $table-border-radius;
79 | }
80 |
81 | thead th, thead td,
82 | caption + tbody tr:first-child td,
83 | > tbody:first-child tr:first-child td {
84 | border-top: 0;
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/public/sass/ui/_tabs.scss:
--------------------------------------------------------------------------------
1 | /* Tabs */
2 |
3 | .tabs {
4 | display: block;
5 | .tab-nav {
6 | margin: 0;
7 | padding: 0;
8 | border-bottom: 1px solid darken($default-color, 5%);
9 | > li {
10 | display: inline-block;
11 | width: auto;
12 | padding: 0;
13 | margin: 0 $gutter 0 0;
14 | cursor: default;
15 | top: 1px;
16 | @include box-shadow(0 1px 0 $white);
17 | > a {
18 | display: block;
19 | width: auto;
20 | padding: 0 $norm;
21 | margin: 0;
22 | color: $body-font-color;
23 | font-family: $font-family;
24 | font-weight: $tabs-font-weight;
25 | border: 1px solid darken($default-color, 5%);
26 | border-width: 1px 1px 0 1px;
27 | text-shadow: 0 1px 1px lighten($default-color, 5%);
28 | background: $default-color;
29 | cursor: pointer;
30 | @include border-radius($button-radius $button-radius 0 0);
31 | @include line-and-height($tab-height);
32 | &:hover {
33 | text-decoration: none;
34 | background: lighten($default-color, 1%);
35 | }
36 | &:active {
37 | background: darken($default-color, 2%);
38 | }
39 | }
40 | &.active > a {
41 | @include line-and-height($tab-height + 1);
42 | background: $white;
43 | cursor: default;
44 | }
45 | &:last-child {
46 | margin-right: 0;
47 | }
48 | }
49 | }
50 | .tab-content {
51 | display: none;
52 | padding: 20px 10px;
53 | &.active {
54 | display: block;
55 | }
56 | }
57 | &.pill .tab-nav {
58 | width: 100%; /* remove if you dont want the tabs to span the full container width */
59 | display: table;
60 | overflow: hidden;
61 | border: 1px solid darken($default-color, 5%);
62 | @include border-radius($button-radius);
63 | > li {
64 | display: table-cell;
65 | margin: 0;
66 | margin-left: -4px;
67 | text-align: center;
68 | top: 0;
69 | &:first-child {
70 | margin-left: 0;
71 | }
72 | > a {
73 | border: none;
74 | border-right: 1px solid darken($default-color, 5%);
75 | @include border-radius(0);
76 | @include line-and-height($tab-height);
77 | }
78 | &:last-child > a {
79 | border-right:none;
80 | }
81 | }
82 | }
83 | &.vertical {
84 | .tab-nav {
85 | border: none;
86 | > li {
87 | display: block;
88 | margin: 0;
89 | margin-bottom: 5px;
90 | &.active {
91 | position: relative;
92 | z-index: 99;
93 | > a {
94 | border-right: 1px solid $global-bg-color;
95 | }
96 | }
97 | > a {
98 | border: 1px solid darken($default-color, 5%);
99 | @include border-radius($button-radius 0 0 $button-radius);
100 | }
101 | }
102 | }
103 | .tab-content {
104 | padding: 10px 0 30px 20px;
105 | margin-left: -1px;
106 | border-left: 1px solid darken($default-color, 5%);
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/public/sass/ui/_toggles.scss:
--------------------------------------------------------------------------------
1 |
2 | .drawer {
3 | position: relative;
4 | width: 100%;
5 | max-height: 0;
6 | background: $drawer-background-color;
7 | @include box-shadow(
8 | inset $drawer-inner-shadow-x-offset #{-$drawer-inner-shadow-y-offset} $drawer-inner-shadow-blur $drawer-inner-shadow-color,
9 | inset $drawer-inner-shadow-x-offset $drawer-inner-shadow-y-offset $drawer-inner-shadow-blur $drawer-inner-shadow-color);
10 | ;
11 | overflow: hidden;
12 | @include transition-duration(.3s);
13 | &.active {
14 | height: auto;
15 | max-height: 800px;
16 | @include transition-duration(.5s);
17 | }
18 | }
19 |
20 | .modal {
21 | width: 100%;
22 | height: 100%;
23 | position: fixed;
24 | top: 0;
25 | left: 0;
26 | z-index: -999999;
27 | background: rgb(0, 0, 0);
28 | background: $modal-overlay-color;
29 | > .content {
30 | width: 50%;
31 | min-height: 50%;
32 | max-height: 65%;
33 | position: relative;
34 | top: 25%;
35 | margin: 0 auto;
36 | padding: $gutter-in-px;
37 | background: $modal-window-color;
38 | z-index: 2;
39 | overflow: auto;
40 | @include respond(portrait-tablets) {
41 | width: 80%;
42 | min-height: 80%;
43 | max-height: 80%;
44 | top: 10%;
45 | }
46 | @include respond(all-phones) {
47 | width: 92.5%;
48 | min-height: 92.5%;
49 | max-height: 92.5%;
50 | top: 3.75%;
51 | }
52 | > .close {
53 | position: absolute;
54 | top: 10px;
55 | right: 10px;
56 | cursor: pointer;
57 | }
58 | }
59 | &, > .content {
60 | @include opacity(0);
61 | @include transition-duration(.3s);
62 | }
63 | &.active {
64 | z-index: 999999;
65 | &, > .content {
66 | @include opacity(1);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/public/sass/ui/_video.scss:
--------------------------------------------------------------------------------
1 | body .video {
2 | width: 100%;
3 | position: relative;
4 | height: 0;
5 | padding-bottom: 56.25%;
6 | &.twitch, &.youtube.show_controls {
7 | padding-top: 30px;
8 | // Use .show_controls f you want the play/pause controls
9 | // to show before the video plays, like on older youtube.
10 | }
11 | &.youtube, &.vimeo {
12 | // Nothing goes here anymore. Both use overlay transports.
13 | }
14 | }
15 |
16 | .video > video, .video > iframe, .video > object, .video > embed {
17 | position: absolute;
18 | top: 0;
19 | left: 0;
20 | width: 100%;
21 | height: 100%;
22 | }
23 |
--------------------------------------------------------------------------------
/public/sass/var/_lists.scss:
--------------------------------------------------------------------------------
1 | // Lists
2 |
3 | // UI Coloring List
4 | $ui-coloring:
5 | primary $primary-color $primary-hover-color,
6 | secondary $secondary-color $secondary-hover-color,
7 | default $default-color $default-hover-color,
8 | info $info-color $info-hover-color,
9 | danger $danger-color $danger-hover-color,
10 | warning $warning-color $warning-hover-color,
11 | success $success-color $success-hover-color;
12 |
13 | // UI Styling List
14 | $styling: metro $metro-radius, pretty $button-radius;
15 |
16 |
17 | // Form field types
18 | $field-types: text, email, password, numeric, search, combined, prepend, append, double;
19 |
20 | $field-sizes: xnarrow, narrow, normal, wide, xwide, xxwide;
--------------------------------------------------------------------------------
/public/sass/var/_settings.scss:
--------------------------------------------------------------------------------
1 | // Welcome to Gumby 2.0 Settings.
2 | // Happy Tinkering!
3 |
4 |
5 | // Grid Settings
6 | $row-max-width: 940px !default; // 940px
7 | $gutter-in-px: 20px !default; // 20px
8 | $cols: 12 !default; // 12 Column Default Grid
9 | $hybrid: 16 !default; // 16 Column Default Hybrid Grid
10 |
11 | // Responsiveness Settings
12 | $min-device-width: 320px; // iPhone Portrait
13 | $tablet-device-width: 768px; // iPad Portrait
14 | $document-width: $row-max-width; // Default Document
15 | $max-device-width: 2880px; // Max Document Size
16 |
17 | // Spacing
18 | $nav-distance: 0px; // Navigation distance from the top of the viewport
19 |
20 | // Typography
21 | $font-family: "Open Sans";
22 | $font-style-italic: italic;
23 | $icons: entypo;
24 | $font-smoothing: antialiased;
25 |
26 | // Font Weights
27 | $font-weight-bold: 700;
28 | $font-weight-semibold: 600;
29 | $font-weight-medium: 400;
30 | $font-weight-regular: 400;
31 | $font-weight-light: 300;
32 | $font-weight-thin: 300;
33 |
34 | $header-font-weight: $font-weight-thin;
35 | $body-font-weight: $font-weight-regular;
36 | $type-font-weight: $font-weight-regular;
37 | $link-font-weight: $font-weight-regular;
38 | $button-font-weight: $font-weight-semibold;
39 | $tabs-font-weight: $font-weight-semibold;
40 |
41 | // Vertical Rhythm Spacing
42 | $base-line-height: ms(1) !default;
43 | $rhythm-spacing: .168;
44 | $rhythm-height: .711;
45 |
46 | // Modular Scale Settings
47 | // http://www.modularscale.com by Tim Brown
48 | // https://github.com/scottkellum/modular-scale
49 | $ratio: golden(); // Ratio for Modular Scale
50 | $base-font-size: 16px !default;
51 | $importantNum: 78px !default;
52 | $base-size: $base-font-size $importantNum;
53 | // Gumby Default Scale Values: 16, 18, 26, 30, 42, 48, 68, 78, 110, 126;
54 |
55 | // Sizing
56 | $xsmall: ms(-2);
57 | $small: ms(-1);
58 | $norm: ms(0); // $base-font-size (16px == default)
59 | $med: ms(1);
60 | $large: ms(2);
61 | $larger: ms(3);
62 | $xlarge: ms(4);
63 | $xxlarge: ms(5);
64 | $xxxlarge: ms(6);
65 | $reallybig: ms(8);
66 | $tremendous: ms(9);
67 | $absurd: ms(10);
68 |
69 | // Typography Colors
70 | $header-font-color: #444444 !default;
71 | $header-link-color: #d04526 !default;
72 | $header-link-hover-color: #c03d20 !default;
73 | $body-font-color: #555555 !default;
74 | $body-link-color: #d04526 !default;
75 | $body-link-hover-color: #c03d20 !default;
76 |
77 | // User Interface Colors
78 | $global-bg-color: #fff;
79 | $navbar-color: #4a4d50;
80 | $navbar-link-color: #fff;
81 |
82 | $primary-color: #3085d6;
83 | $secondary-color: #42a35a;
84 | $default-color: #f2f2f2;
85 | $info-color: #4a4d50;
86 | $danger-color: #ca3838;
87 | $warning-color: #f6b83f;
88 | $success-color: #58c026;
89 |
90 | $primary-hover-color: #58b2fa;
91 | $secondary-hover-color: #6dbb80;
92 | $default-hover-color: #ffffff;
93 | $info-hover-color: #868d92;
94 | $danger-hover-color: #f14f4f;
95 | $warning-hover-color: #fdd27f;
96 | $success-hover-color: #66d92f;
97 |
98 | $horizontal-rule-color: #ccc !default;
99 |
100 | $black: #000;
101 | $white: #fff;
102 |
103 | // Borders
104 | $button-radius: 4px !default;
105 | $metro-radius: 0 !default;
106 | $bigger-radius: 8px;
107 |
108 | // Buttons
109 | // Font Sizing
110 | $xlarge-button-font-size: $larger;
111 | $large-button-font-size: $large;
112 | $medium-button-font-size: $norm;
113 | $small-button-font-size: $small;
114 | // Padding
115 | $default-button-padding: $med;
116 | // Height
117 | $default-button-height: 36px;
118 |
119 | // Tabs
120 | $tab-height: 42px;
121 |
122 | // Drawers & Modals
123 | $modal-overlay-color: rgba(0, 0, 0, 0.8);
124 | $modal-window-color: $white;
125 | $drawer-background-color: #3e4144;
126 | $drawer-inner-shadow-x-offset: 0;
127 | $drawer-inner-shadow-y-offset: 2px;
128 | $drawer-inner-shadow-blur: 5px;
129 | $drawer-inner-shadow-color: #313436;
130 |
131 | // Tables
132 | $table-bgcolor: #fff;
133 | $table-thead-bgcolor: $primary-color;
134 | $table-row-first-cell-font-weight: bold;
135 | $table-border-size: 1px;
136 | $table-border-style: solid;
137 | $table-border-color: #e5e5e5;
138 | $table-cell-border-size: 1px;
139 | $table-cell-border-color: #e5e5e5;
140 | $table-cell-border-style: solid;
141 | // .rounded
142 | $table-border-radius: 4px;
143 | // .striped
144 | $table-stripe-bgcolor: #e5e5e5;
145 |
146 | // Floats
147 | $default-float: left;
148 | $switch-float: right;
149 |
150 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Implementing Laravel
2 |
3 | The companion application code to my e-book [Implementing Laravel](https://leanpub.com/implementinglaravel).
4 |
5 | ## Setting Up
6 |
7 | See files `public/.htaccess` and `bootstrap/start`.
8 |
9 | See `composer.json` for PSR-0 autoloading and `app/Impl` for application library.
10 |
11 | ## Repository Pattern
12 |
13 | See files under `app/Impl/Repo`
14 |
15 | ## Repository Pattern + Cache Layer
16 |
17 | See files under `app/Impl/Repo` and `app/Impl/Service/Cache`
18 |
19 | ## Validation as a Service
20 |
21 | See files under `app/Impl/Service/Valiation`.
22 |
23 | ## Form Processing
24 |
25 | See files under `app/Impl/Service/Form`.
26 |
27 | ## Error Handling
28 |
29 | See files under `app/Impl/Exception`.
30 |
31 | ## Using Packages
32 |
33 | ### Notification
34 |
35 | See files under `app/Impl/Service/Notification`.
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 |