Repository: alfonsomga/symfony.demo.on.roids Branch: master Commit: f01514d70f28 Files: 379 Total size: 1.7 MB Directory structure: gitextract_ljy0z57z/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── app/ │ ├── .htaccess │ ├── AppCache.php │ ├── AppKernel.php │ ├── Resources/ │ │ ├── TwigBundle/ │ │ │ └── views/ │ │ │ └── Exception/ │ │ │ ├── error.html.twig │ │ │ ├── error403.html.twig │ │ │ ├── error404.html.twig │ │ │ └── error500.html.twig │ │ ├── assets/ │ │ │ ├── css/ │ │ │ │ ├── font-lato.css │ │ │ │ └── highlight-solarized-light.css │ │ │ ├── js/ │ │ │ │ ├── bootstrap-3.3.4.js │ │ │ │ ├── highlight.pack.js │ │ │ │ ├── jquery-2.1.4.js │ │ │ │ └── main.js │ │ │ └── scss/ │ │ │ ├── bootstrap/ │ │ │ │ ├── _alerts.scss │ │ │ │ ├── _badges.scss │ │ │ │ ├── _breadcrumbs.scss │ │ │ │ ├── _button-groups.scss │ │ │ │ ├── _buttons.scss │ │ │ │ ├── _carousel.scss │ │ │ │ ├── _close.scss │ │ │ │ ├── _code.scss │ │ │ │ ├── _component-animations.scss │ │ │ │ ├── _dropdowns.scss │ │ │ │ ├── _forms.scss │ │ │ │ ├── _glyphicons.scss │ │ │ │ ├── _grid.scss │ │ │ │ ├── _input-groups.scss │ │ │ │ ├── _jumbotron.scss │ │ │ │ ├── _labels.scss │ │ │ │ ├── _list-group.scss │ │ │ │ ├── _media.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _modals.scss │ │ │ │ ├── _navbar.scss │ │ │ │ ├── _navs.scss │ │ │ │ ├── _normalize.scss │ │ │ │ ├── _pager.scss │ │ │ │ ├── _pagination.scss │ │ │ │ ├── _panels.scss │ │ │ │ ├── _popovers.scss │ │ │ │ ├── _print.scss │ │ │ │ ├── _progress-bars.scss │ │ │ │ ├── _responsive-embed.scss │ │ │ │ ├── _responsive-utilities.scss │ │ │ │ ├── _scaffolding.scss │ │ │ │ ├── _tables.scss │ │ │ │ ├── _theme.scss │ │ │ │ ├── _thumbnails.scss │ │ │ │ ├── _tooltip.scss │ │ │ │ ├── _type.scss │ │ │ │ ├── _utilities.scss │ │ │ │ ├── _variables.scss │ │ │ │ ├── _wells.scss │ │ │ │ ├── flatly-theme/ │ │ │ │ │ ├── _bootswatch.scss │ │ │ │ │ └── _variables.scss │ │ │ │ └── mixins/ │ │ │ │ ├── _alerts.scss │ │ │ │ ├── _background-variant.scss │ │ │ │ ├── _border-radius.scss │ │ │ │ ├── _buttons.scss │ │ │ │ ├── _center-block.scss │ │ │ │ ├── _clearfix.scss │ │ │ │ ├── _forms.scss │ │ │ │ ├── _gradients.scss │ │ │ │ ├── _grid-framework.scss │ │ │ │ ├── _grid.scss │ │ │ │ ├── _hide-text.scss │ │ │ │ ├── _image.scss │ │ │ │ ├── _labels.scss │ │ │ │ ├── _list-group.scss │ │ │ │ ├── _nav-divider.scss │ │ │ │ ├── _nav-vertical-align.scss │ │ │ │ ├── _opacity.scss │ │ │ │ ├── _pagination.scss │ │ │ │ ├── _panels.scss │ │ │ │ ├── _progress-bar.scss │ │ │ │ ├── _reset-filter.scss │ │ │ │ ├── _resize.scss │ │ │ │ ├── _responsive-visibility.scss │ │ │ │ ├── _size.scss │ │ │ │ ├── _tab-focus.scss │ │ │ │ ├── _table-row.scss │ │ │ │ ├── _text-emphasis.scss │ │ │ │ ├── _text-overflow.scss │ │ │ │ └── _vendor-prefixes.scss │ │ │ ├── bootstrap.scss │ │ │ ├── font-awesome/ │ │ │ │ ├── _animated.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _stacked.scss │ │ │ │ └── _variables.scss │ │ │ ├── font-awesome.scss │ │ │ └── main.scss │ │ ├── translations/ │ │ │ ├── messages.ca.xliff │ │ │ ├── messages.cs.xliff │ │ │ ├── messages.de.xliff │ │ │ ├── messages.en.xliff │ │ │ ├── messages.es.xliff │ │ │ ├── messages.fr.xliff │ │ │ ├── messages.id.xliff │ │ │ ├── messages.it.xliff │ │ │ ├── messages.ja.xliff │ │ │ ├── messages.nl.xliff │ │ │ ├── messages.pl.xliff │ │ │ ├── messages.pt_BR.xliff │ │ │ ├── messages.ro.xliff │ │ │ ├── messages.ru.xliff │ │ │ ├── messages.uk.xliff │ │ │ ├── validators.ca.xliff │ │ │ ├── validators.cs.xliff │ │ │ ├── validators.de.xliff │ │ │ ├── validators.en.xliff │ │ │ ├── validators.es.xliff │ │ │ ├── validators.fr.xliff │ │ │ ├── validators.id.xliff │ │ │ ├── validators.it.xliff │ │ │ ├── validators.ja.xliff │ │ │ ├── validators.nl.xliff │ │ │ ├── validators.pl.xliff │ │ │ ├── validators.pt_BR.xliff │ │ │ ├── validators.ro.xliff │ │ │ ├── validators.ru.xliff │ │ │ └── validators.uk.xliff │ │ └── views/ │ │ ├── admin/ │ │ │ ├── blog/ │ │ │ │ ├── _form.html.twig │ │ │ │ ├── edit.html.twig │ │ │ │ ├── index.html.twig │ │ │ │ ├── new.html.twig │ │ │ │ └── show.html.twig │ │ │ └── layout.html.twig │ │ ├── base.html.twig │ │ ├── blog/ │ │ │ ├── _comment_form.html.twig │ │ │ ├── _delete_post_confirmation.html.twig │ │ │ ├── comment_form_error.html.twig │ │ │ ├── index.html.twig │ │ │ └── post_show.html.twig │ │ ├── default/ │ │ │ ├── _flash_messages.html.twig │ │ │ └── homepage.html.twig │ │ ├── form/ │ │ │ └── fields.html.twig │ │ └── security/ │ │ └── login.html.twig │ ├── SymfonyRequirements.php │ ├── autoload.php │ ├── check.php │ ├── config/ │ │ ├── config.yml │ │ ├── config_dev.yml │ │ ├── config_prod.yml │ │ ├── config_test.yml │ │ ├── parameters.yml.dist │ │ ├── routing.yml │ │ ├── routing_dev.yml │ │ ├── security.yml │ │ └── services.yml │ ├── console │ ├── logs/ │ │ └── .gitkeep │ └── phpunit.xml.dist ├── composer.json ├── src/ │ ├── .htaccess │ ├── AppBundle/ │ │ ├── AppBundle.php │ │ ├── Command/ │ │ │ ├── AddUserCommand.php │ │ │ ├── DeleteUserCommand.php │ │ │ └── ListUsersCommand.php │ │ ├── Controller/ │ │ │ ├── Admin/ │ │ │ │ └── BlogController.php │ │ │ ├── BlogController.php │ │ │ └── SecurityController.php │ │ ├── DataFixtures/ │ │ │ └── ORM/ │ │ │ └── LoadFixtures.php │ │ ├── Entity/ │ │ │ ├── Comment.php │ │ │ ├── Post.php │ │ │ └── User.php │ │ ├── EventListener/ │ │ │ └── RedirectToPreferredLocaleListener.php │ │ ├── Form/ │ │ │ ├── CommentType.php │ │ │ ├── PostType.php │ │ │ └── Type/ │ │ │ └── DateTimePickerType.php │ │ ├── Repository/ │ │ │ ├── PostRepository.php │ │ │ └── UserRepository.php │ │ ├── Tests/ │ │ │ ├── Controller/ │ │ │ │ ├── Admin/ │ │ │ │ │ └── BlogControllerTest.php │ │ │ │ ├── BlogControllerTest.php │ │ │ │ └── DefaultControllerTest.php │ │ │ └── Utils/ │ │ │ └── SluggerTest.php │ │ ├── Twig/ │ │ │ └── AppExtension.php │ │ └── Utils/ │ │ ├── Markdown.php │ │ ├── MomentFormatConverter.php │ │ └── Slugger.php │ ├── CodeExplorerBundle/ │ │ ├── CodeExplorerBundle.php │ │ ├── DependencyInjection/ │ │ │ └── CodeExplorerExtension.php │ │ ├── EventListener/ │ │ │ └── ControllerListener.php │ │ ├── Resources/ │ │ │ ├── config/ │ │ │ │ └── services.yml │ │ │ └── views/ │ │ │ └── source_code.html.twig │ │ └── Twig/ │ │ └── SourceCodeExtension.php │ ├── ElasticSearchBundle/ │ │ ├── Controller/ │ │ │ └── ElasticSearchController.php │ │ ├── ElasticSearchBundle.php │ │ ├── Resources/ │ │ │ ├── config/ │ │ │ │ ├── config.yml │ │ │ │ ├── routing.yml │ │ │ │ └── services.yml │ │ │ └── views/ │ │ │ └── elastica_search_results.html.twig │ │ └── Tests/ │ │ └── Controller/ │ │ └── ElasticSearchControllerTest.php │ ├── OAuthBundle/ │ │ ├── Controller/ │ │ │ └── OAuthController.php │ │ ├── DependencyInjection/ │ │ │ ├── Configuration.php │ │ │ └── OAuthExtension.php │ │ ├── OAuthBundle.php │ │ ├── Provider/ │ │ │ └── UserProvider.php │ │ ├── Resources/ │ │ │ └── config/ │ │ │ ├── config.yml │ │ │ ├── routing.yml │ │ │ └── services.yml │ │ ├── Services/ │ │ │ └── AuthenticationSuccessHandler.php │ │ └── Tests/ │ │ ├── Controller/ │ │ │ └── OAuthControllerTest.php │ │ └── OAuthTest.php │ ├── RabbitMQBundle/ │ │ ├── Controller/ │ │ │ └── RabbitMQController.php │ │ ├── DependencyInjection/ │ │ │ ├── Configuration.php │ │ │ └── RabbitMQExtension.php │ │ ├── RabbitMQBundle.php │ │ ├── Resources/ │ │ │ ├── config/ │ │ │ │ ├── config.yml │ │ │ │ ├── config_dev.yml │ │ │ │ ├── config_test.yml │ │ │ │ ├── routing.yml │ │ │ │ └── services.yml │ │ │ └── views/ │ │ │ └── pdf_post_view.html.twig │ │ ├── Services/ │ │ │ ├── PDFGenerator.php │ │ │ └── RabbitMQ/ │ │ │ └── PDFGeneratorConsumer.php │ │ └── Tests/ │ │ └── Controller/ │ │ └── RabbitMQControllerTest.php │ ├── RedisBundle/ │ │ ├── Controller/ │ │ │ └── RedisController.php │ │ ├── RedisBundle.php │ │ ├── Resources/ │ │ │ ├── config/ │ │ │ │ ├── config.yml │ │ │ │ ├── routing.yml │ │ │ │ └── services.yml │ │ │ └── views/ │ │ │ └── blog/ │ │ │ └── top_posts.html.twig │ │ └── Tests/ │ │ └── Controller/ │ │ └── RedisControllerTest.php │ └── RestBundle/ │ ├── Controller/ │ │ └── RestController.php │ ├── DependencyInjection/ │ │ ├── Configuration.php │ │ └── RestExtension.php │ ├── Form/ │ │ ├── CommentRestType.php │ │ └── PostRestType.php │ ├── Resources/ │ │ ├── config/ │ │ │ ├── config.yml │ │ │ ├── routing.yml │ │ │ ├── serializer/ │ │ │ │ └── AppBundle/ │ │ │ │ ├── Entity.Comment.yml │ │ │ │ └── Entity.Post.yml │ │ │ └── services.yml │ │ └── views/ │ │ ├── Comment/ │ │ │ ├── editComment.html.twig │ │ │ ├── getComment.html.twig │ │ │ ├── getComments.html.twig │ │ │ └── newComment.html.twig │ │ ├── Post/ │ │ │ ├── editPost.html.twig │ │ │ ├── getPost.html.twig │ │ │ ├── getPosts.html.twig │ │ │ └── newPost.html.twig │ │ ├── api_base_layout.html.twig │ │ └── api_index.html.twig │ ├── RestBundle.php │ └── Tests/ │ └── Controller/ │ └── RestControllerTest.php ├── vagrant/ │ ├── Vagrantfile │ └── ansible/ │ ├── hosts_inventory │ ├── roles/ │ │ ├── Stouts.rabbitmq/ │ │ │ ├── .bumpversion.cfg │ │ │ ├── .travis.yml │ │ │ ├── CONTRIBUTORS │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── handlers/ │ │ │ │ └── main.yml │ │ │ ├── meta/ │ │ │ │ ├── .galaxy_install_info │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ ├── install.deb.yml │ │ │ │ ├── main.yml │ │ │ │ └── rabbitmq.yml │ │ │ └── test.yml │ │ ├── geerlingguy.composer/ │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── meta/ │ │ │ │ ├── .galaxy_install_info │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ ├── global-require.yml │ │ │ │ └── main.yml │ │ │ ├── templates/ │ │ │ │ ├── auth.json.j2 │ │ │ │ └── composer.sh.j2 │ │ │ └── tests/ │ │ │ ├── inventory │ │ │ ├── test-global-require.yml │ │ │ └── test.yml │ │ ├── geerlingguy.nginx/ │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── handlers/ │ │ │ │ └── main.yml │ │ │ ├── meta/ │ │ │ │ ├── .galaxy_install_info │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ ├── main.yml │ │ │ │ ├── setup-Debian.yml │ │ │ │ ├── setup-RedHat.yml │ │ │ │ └── vhosts.yml │ │ │ ├── templates/ │ │ │ │ ├── nginx.conf.j2 │ │ │ │ ├── nginx.repo.j2 │ │ │ │ └── vhosts.j2 │ │ │ ├── tests/ │ │ │ │ ├── inventory │ │ │ │ └── test.yml │ │ │ └── vars/ │ │ │ ├── Debian.yml │ │ │ └── RedHat.yml │ │ ├── geerlingguy.php/ │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── handlers/ │ │ │ │ └── main.yml │ │ │ ├── meta/ │ │ │ │ ├── .galaxy_install_info │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ ├── configure-fpm.yml │ │ │ │ ├── configure.yml │ │ │ │ ├── install-from-source.yml │ │ │ │ ├── main.yml │ │ │ │ ├── setup-Debian.yml │ │ │ │ └── setup-RedHat.yml │ │ │ ├── templates/ │ │ │ │ ├── apc.ini.j2 │ │ │ │ ├── fpm-init.j2 │ │ │ │ ├── opcache.ini.j2 │ │ │ │ ├── php-fpm.conf.j2 │ │ │ │ └── php.ini.j2 │ │ │ ├── tests/ │ │ │ │ ├── inventory │ │ │ │ ├── test-install-from-source.yml │ │ │ │ └── test-install-package.yml │ │ │ └── vars/ │ │ │ ├── Debian.yml │ │ │ └── RedHat.yml │ │ ├── geerlingguy.redis/ │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── handlers/ │ │ │ │ └── main.yml │ │ │ ├── meta/ │ │ │ │ ├── .galaxy_install_info │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ ├── main.yml │ │ │ │ ├── setup-Debian.yml │ │ │ │ └── setup-RedHat.yml │ │ │ ├── templates/ │ │ │ │ └── redis.conf.j2 │ │ │ ├── tests/ │ │ │ │ ├── inventory │ │ │ │ └── test.yml │ │ │ └── vars/ │ │ │ ├── Debian.yml │ │ │ └── RedHat.yml │ │ └── gpstathis.elasticsearch/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── README.md │ │ ├── Vagrantfile │ │ ├── defaults/ │ │ │ └── main.yml │ │ ├── handlers/ │ │ │ └── main.yml │ │ ├── meta/ │ │ │ ├── .galaxy_install_info │ │ │ └── main.yml │ │ ├── tasks/ │ │ │ ├── aws.yml │ │ │ ├── custom-jars.yml │ │ │ ├── elastic-install.yml │ │ │ ├── java.yml │ │ │ ├── main.yml │ │ │ ├── marvel.yml │ │ │ ├── plugins.yml │ │ │ ├── post-run.yml │ │ │ ├── spm.yml │ │ │ └── timezone.yml │ │ ├── templates/ │ │ │ ├── elasticsearch.default.j2 │ │ │ ├── elasticsearch.in.sh.j2 │ │ │ └── elasticsearch.yml.j2 │ │ ├── tests/ │ │ │ ├── ansible.cfg │ │ │ ├── elastic_test.sh │ │ │ ├── local.ini │ │ │ ├── test1.yml │ │ │ └── test1_var.yml │ │ ├── vagrant-inventory.ini │ │ ├── vagrant-main.yml │ │ └── vars/ │ │ ├── sample.yml │ │ └── vagrant.yml │ ├── setup.yml │ └── templates/ │ └── rabbitmq-consumers.j2 └── web/ ├── .htaccess ├── app.php ├── app_dev.php ├── config.php ├── css/ │ ├── api.css │ └── app.css ├── fonts/ │ └── font-awesome/ │ └── FontAwesome.otf ├── js/ │ └── app.js └── robots.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Cache and logs (Symfony2) /app/cache/* /app/logs/* !app/cache/.gitkeep !app/logs/.gitkeep # Cache and logs (Symfony3) /var/cache/* /var/logs/* !var/cache/.gitkeep !var/logs/.gitkeep # Parameters /app/config/parameters.yml /app/config/parameters.ini # Managed by Composer /app/bootstrap.php.cache /var/bootstrap.php.cache /bin/* !bin/console !bin/symfony_requirements /vendor/ # Assets and user uploads /web/bundles/ /web/uploads/ /web/downloads/ # PHPUnit /app/phpunit.xml /phpunit.xml # Build data /build/ # Composer PHAR /composer.phar # Vagrant /vagrant/.vagrant/ # IDE /.idea/ ================================================ FILE: .travis.yml ================================================ language: php sudo: required cache: directories: - $HOME/.composer/cache/files matrix: fast_finish: true include: - php: 5.5 - php: 5.6 - php: 7.0 allow_failures: - php: 7.0 services: - redis-server - elasticsearch - rabbitmq before_install: - wget http://download.gna.org/wkhtmltopdf/0.12/0.12.3/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz - tar xf wkhtmltox-0.12.3_linux-generic-amd64.tar.xz - sudo mv wkhtmltox/bin/wkhtmltopdf /usr/local/bin/ - chmod +x /usr/local/bin/wkhtmltopdf install: - composer install before_script: - sudo rabbitmqctl add_user admin symfony.demo.on.roids - app/console rabbitmq:consumer generate_pdf --env=test & script: - phpunit -c app ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Alfonso M. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # The Symfony Demo Application on Roids! [![Build Status](https://travis-ci.org/alfonsomga/symfony.demo.on.roids.svg)](https://travis-ci.org/alfonsomga/symfony.demo.on.roids)[![Total Downloads](https://poser.pugx.org/alfonsomga/symfony.demo.on.roids/downloads)](https://packagist.org/packages/alfonsomga/symfony.demo.on.roids)[![PHP Version](https://img.shields.io/badge/php-5.5.21+-ff69b4.svg)](https://packagist.org/packages/alfonsomga/symfony.demo.on.roids)[![License](https://poser.pugx.org/alfonsomga/symfony.demo.on.roids/license)](https://packagist.org/packages/alfonsomga/symfony.demo.on.roids) The "Symfony Demo Application on Roids" is an application based on the original [**Symfony Demo Application**](https://github.com/symfony/symfony-demo) that includes extra features using technologies like [**Elasticsearch**](https://www.elastic.co/products/elasticsearch), [**OAuth**](http://oauth.net/), [**RabbitMQ**](https://www.rabbitmq.com/), [**Redis**](http://redis.io/) and a [**RESTful API**](https://en.wikipedia.org/wiki/Representational_state_transfer) + [**HATEOAS**](https://en.wikipedia.org/wiki/HATEOAS).

## Table of Contents - [Setting up & running the demo with Vagrant + Ansible](#setting-up--running-the-demo-with-vagrant--ansible) - [Prerequisites](#prerequisites) - [Instructions](#instructions) - [RESTful API + HATEOAS](#restful-api--hateoas) - [Elasticsearch](#elasticsearch) - [OAuth](#oauth) - [RabbitMQ](#rabbitmq) - [Redis](#redis) ## Setting up & running the demo with [**Vagrant**](https://www.vagrantup.com/) + [**Ansible**](http://www.ansible.com/) ### Prerequisites - [Vagrant](https://www.vagrantup.com/downloads.html) installed - [Ansible] (http://docs.ansible.com/ansible/intro_installation.html) installed - [Virtualbox](https://www.virtualbox.org/wiki/Downloads) installed ### Instructions 1. ``git clone https://github.com/alfonsomga/symfony.demo.on.roids.git`` 2. ``cd symfony.demo.on.roids/vagrant/`` 3. ``vagrant up`` 4. Wait until Ansible installs and configure everything 5. Finally navigate to **http://192.168.50.88** to browse the app Congratulations! You're now ready to use The Symfony Demo On Roids. ## RESTful API + HATEOAS A RESTful API + HATEOAS has been implemented for expose the resources. Different formats are available for manage the data: HTML, JSON and XML. **Related urls:** - **API Index page** - **API Documentation** **Bundles used:** - [**FOSRestBundle**](https://github.com/FriendsOfSymfony/FOSRestBundle) - [**JMSSerializerBundle**](https://github.com/schmittjoh/JMSSerializerBundle) - [**NelmioApiDocBundle**](https://github.com/nelmio/NelmioApiDocBundle) - [**FOSHttpCacheBundle**](https://github.com/FriendsOfSymfony/FOSHttpCacheBundle) - [**BazingaHateoasBundle**](https://github.com/willdurand/BazingaHateoasBundle) - [**BazingaRestExtraBundle**](https://github.com/willdurand/BazingaRestExtraBundle) ## Elasticsearch Elasticsearch has been used for add a simple search form and show relevant results based on the user search query. **Related urls:** - **Elasticsearch Admin panel** - **Elasticsearch app search page (results for ``Lorem ipsum``)** **Bundles used:** - [**FOSElasticaBundle**](https://github.com/FriendsOfSymfony/FOSElasticaBundle) ## OAuth OAuth has been used for link/unlink an account from an OAuth provider (GitHub in this case) to an existent backend account and allows to log in directly to the backend from a GitHub account. To use this feature you must [register a new OAuth Application](https://github.com/settings/applications/new) and edit the [parameters.yml.dist](https://github.com/alfonsomga/symfony.demo.on.roids/blob/master/app/config/parameters.yml.dist) file with your app ``client id`` and ``client secret`` token. **Related urls:** - **Login page (click on ``Sign in with GitHub``)** - **Manage your OAuth account (``Link`` or ``Unlink`` ``Github Account``)** **Bundles used:** - [**HWIOAuthBundle**](https://github.com/hwi/HWIOAuthBundle) ## RabbitMQ RabbitMQ has been used for generate a PDF file based on the article content from a consumer in a scalable way. **Related urls:** - **RabbitMQ Admin panel (User: ``admin`` password: ``symfony.demo.on.roids``)** - **Post page (click on ``Download post as PDF``)** **Bundles used:** - [**RabbitMqBundle**](https://github.com/videlalvaro/RabbitMqBundle) - [**KnpSnappyBundle**](https://github.com/KnpLabs/KnpSnappyBundle) ## Redis Redis has been used for show the top 5 popular posts and set a cache lifetime of 3600 seconds. **Related urls:** - **Top 5 popular posts** **Bundles used:** - [**SncRedisBundle**](https://github.com/snc/SncRedisBundle) ================================================ FILE: app/.htaccess ================================================ Require all denied Order deny,allow Deny from all ================================================ FILE: app/AppCache.php ================================================ getEnvironment(), array('dev', 'test'))) { $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(); $bundles[] = new Liip\FunctionalTestBundle\LiipFunctionalTestBundle(); } return $bundles; } public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); } } ================================================ FILE: app/Resources/TwigBundle/views/Exception/error.html.twig ================================================ {# This template is used to render any error different from 403, 404 and 500. This is the simplest way to customize error pages in Symfony applications. In case you need it, you can also hook into the internal exception handling made by Symfony. This allows you to perform advanced tasks and even recover your application from some errors. See http://symfony.com/doc/current/cookbook/controller/error_pages.html #} {% extends 'base.html.twig' %} {% block body_id 'error' %} {% block main %}

Error

There was an unknown error (HTTP {{ status_code }}) that prevented to complete your request.

Try loading this page again in some minutes or go back to the homepage.

{% endblock %} {% block sidebar %} {{ parent() }} {{ show_source_code(_self) }} {% endblock %} ================================================ FILE: app/Resources/TwigBundle/views/Exception/error403.html.twig ================================================ {# This template is used to render errors of type HTTP 403 (Forbidden) This is the simplest way to customize error pages in Symfony applications. In case you need it, you can also hook into the internal exception handling made by Symfony. This allows you to perform advanced tasks and even recover your application from some errors. See http://symfony.com/doc/current/cookbook/controller/error_pages.html #} {% extends 'base.html.twig' %} {% block body_id 'error' %} {% block main %}

Error 403

You don't have permission to access to this resource.

Ask your manager or system administrator to grant you access to this resource.

{% endblock %} {% block sidebar %} {{ parent() }} {{ show_source_code(_self) }} {% endblock %} ================================================ FILE: app/Resources/TwigBundle/views/Exception/error404.html.twig ================================================ {# This template is used to render errors of type HTTP 404 (Not Found) This is the simplest way to customize error pages in Symfony applications. In case you need it, you can also hook into the internal exception handling made by Symfony. This allows you to perform advanced tasks and even recover your application from some errors. See http://symfony.com/doc/current/cookbook/controller/error_pages.html #} {% extends 'base.html.twig' %} {% block body_id 'error' %} {% block main %}

Error 404

We couldn't find the page you requested.

Check out any misspelling in the URL or go back to the homepage.

{% endblock %} {% block sidebar %} {{ parent() }} {{ show_source_code(_self) }} {% endblock %} ================================================ FILE: app/Resources/TwigBundle/views/Exception/error500.html.twig ================================================ {# This template is used to render errors of type HTTP 500 (Internal Server Error) This is the simplest way to customize error pages in Symfony applications. In case you need it, you can also hook into the internal exception handling made by Symfony. This allows you to perform advanced tasks and even recover your application from some errors. See http://symfony.com/doc/current/cookbook/controller/error_pages.html #} {% extends 'base.html.twig' %} {% block body_id 'error' %} {% block main %}

Error 500

There was an internal server error.

Try loading this page again in some minutes or go back to the homepage.

{% endblock %} {% block sidebar %} {{ parent() }} {{ show_source_code(_self) }} {% endblock %} ================================================ FILE: app/Resources/assets/css/font-lato.css ================================================ @font-face { font-family: 'Lato'; font-style: normal; font-weight: 400; src: local('Lato Regular'), local('Lato-Regular'), url('../fonts/lato/Lato-Regular.woff') format('woff'); } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 700; src: local('Lato Bold'), local('Lato-Bold'), url('../fonts/lato/Lato-Bold.woff') format('woff'); } @font-face { font-family: 'Lato'; font-style: italic; font-weight: 400; src: local('Lato Italic'), local('Lato-Italic'), url('../fonts/lato/Lato-Italic.woff') format('woff'); } @font-face { font-family: 'Lato'; font-style: italic; font-weight: 700; src: local('Lato Bold Italic'), local('Lato-BoldItalic'), url('../fonts/lato/Lato-BoldItalic.woff') format('woff'); } ================================================ FILE: app/Resources/assets/css/highlight-solarized-light.css ================================================ /* Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull */ .hljs { display: block; overflow-x: auto; padding: 0.5em; background: #fdf6e3; color: #657b83; -webkit-text-size-adjust: none; } .hljs-comment, .diff .hljs-header, .hljs-doctype, .hljs-pi, .lisp .hljs-string, .hljs-javadoc { color: #93a1a1; } /* Solarized Green */ .hljs-keyword, .hljs-winutils, .method, .hljs-addition, .css .hljs-tag, .hljs-request, .hljs-status, .nginx .hljs-title { color: #859900; } /* Solarized Cyan */ .hljs-number, .hljs-command, .hljs-string, .hljs-tag .hljs-value, .hljs-rules .hljs-value, .hljs-phpdoc, .hljs-dartdoc, .tex .hljs-formula, .hljs-regexp, .hljs-hexcolor, .hljs-link_url { color: #2aa198; } /* Solarized Blue */ .hljs-title, .hljs-localvars, .hljs-chunk, .hljs-decorator, .hljs-built_in, .hljs-identifier, .vhdl .hljs-literal, .hljs-id, .css .hljs-function { color: #268bd2; } /* Solarized Yellow */ .hljs-attribute, .hljs-variable, .lisp .hljs-body, .smalltalk .hljs-number, .hljs-constant, .hljs-class .hljs-title, .hljs-parent, .hljs-type, .hljs-link_reference { color: #b58900; } /* Solarized Orange */ .hljs-preprocessor, .hljs-preprocessor .hljs-keyword, .hljs-pragma, .hljs-shebang, .hljs-symbol, .hljs-symbol .hljs-string, .diff .hljs-change, .hljs-special, .hljs-attr_selector, .hljs-subst, .hljs-cdata, .css .hljs-pseudo, .hljs-header { color: #cb4b16; } /* Solarized Red */ .hljs-deletion, .hljs-important { color: #dc322f; } /* Solarized Violet */ .hljs-link_label { color: #6c71c4; } .tex .hljs-formula { background: #eee8d5; } ================================================ FILE: app/Resources/assets/js/bootstrap-3.3.4.js ================================================ /*! * Bootstrap v3.3.4 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } +function ($) { 'use strict'; var version = $.fn.jquery.split(' ')[0].split('.') if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') } }(jQuery); /* ======================================================================== * Bootstrap: transition.js v3.3.4 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() if (!$.support.transition) return $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) } } }) }(jQuery); /* ======================================================================== * Bootstrap: alert.js v3.3.4 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // ALERT CLASS DEFINITION // ====================== var dismiss = '[data-dismiss="alert"]' var Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.VERSION = '3.3.4' Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = $(selector) if (e) e.preventDefault() if (!$parent.length) { $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { // detach from parent, fire event then clean up data $parent.detach().trigger('closed.bs.alert').remove() } $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } // ALERT PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.alert') if (!data) $this.data('bs.alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.alert $.fn.alert = Plugin $.fn.alert.Constructor = Alert // ALERT NO CONFLICT // ================= $.fn.alert.noConflict = function () { $.fn.alert = old return this } // ALERT DATA-API // ============== $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) }(jQuery); /* ======================================================================== * Bootstrap: button.js v3.3.4 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // BUTTON PUBLIC CLASS DEFINITION // ============================== var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, Button.DEFAULTS, options) this.isLoading = false } Button.VERSION = '3.3.4' Button.DEFAULTS = { loadingText: 'loading...' } Button.prototype.setState = function (state) { var d = 'disabled' var $el = this.$element var val = $el.is('input') ? 'val' : 'html' var data = $el.data() state = state + 'Text' if (data.resetText == null) $el.data('resetText', $el[val]()) // push to event loop to allow forms to submit setTimeout($.proxy(function () { $el[val](data[state] == null ? this.options[state] : data[state]) if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d) } else if (this.isLoading) { this.isLoading = false $el.removeClass(d).removeAttr(d) } }, this), 0) } Button.prototype.toggle = function () { var changed = true var $parent = this.$element.closest('[data-toggle="buttons"]') if ($parent.length) { var $input = this.$element.find('input') if ($input.prop('type') == 'radio') { if ($input.prop('checked') && this.$element.hasClass('active')) changed = false else $parent.find('.active').removeClass('active') } if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') } else { this.$element.attr('aria-pressed', !this.$element.hasClass('active')) } if (changed) this.$element.toggleClass('active') } // BUTTON PLUGIN DEFINITION // ======================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.button') var options = typeof option == 'object' && option if (!data) $this.data('bs.button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } var old = $.fn.button $.fn.button = Plugin $.fn.button.Constructor = Button // BUTTON NO CONFLICT // ================== $.fn.button.noConflict = function () { $.fn.button = old return this } // BUTTON DATA-API // =============== $(document) .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { var $btn = $(e.target) if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') Plugin.call($btn, 'toggle') e.preventDefault() }) .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) }) }(jQuery); /* ======================================================================== * Bootstrap: carousel.js v3.3.4 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = null this.sliding = null this.interval = null this.$active = null this.$items = null this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } Carousel.VERSION = '3.3.4' Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', wrap: true, keyboard: true } Carousel.prototype.keydown = function (e) { if (/input|textarea/i.test(e.target.tagName)) return switch (e.which) { case 37: this.prev(); break case 39: this.next(); break default: return } e.preventDefault() } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getItemIndex = function (item) { this.$items = item.parent().children('.item') return this.$items.index(item || this.$active) } Carousel.prototype.getItemForDirection = function (direction, active) { var activeIndex = this.getItemIndex(active) var willWrap = (direction == 'prev' && activeIndex === 0) || (direction == 'next' && activeIndex == (this.$items.length - 1)) if (willWrap && !this.options.wrap) return active var delta = direction == 'prev' ? -1 : 1 var itemIndex = (activeIndex + delta) % this.$items.length return this.$items.eq(itemIndex) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var that = this if ($next.hasClass('active')) return (this.sliding = false) var relatedTarget = $next[0] var slideEvent = $.Event('slide.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) this.$element.trigger(slideEvent) if (slideEvent.isDefaultPrevented()) return this.sliding = true isCycling && this.pause() if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) $nextIndicator && $nextIndicator.addClass('active') } var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" if ($.support.transition && this.$element.hasClass('slide')) { $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one('bsTransitionEnd', function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger(slidEvent) }, 0) }) .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger(slidEvent) } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } var old = $.fn.carousel $.fn.carousel = Plugin $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 if (!$target.hasClass('carousel')) return var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false Plugin.call($target, options) if (slideIndex) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() } $(document) .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) Plugin.call($carousel, $carousel.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: collapse.js v3.3.4 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // COLLAPSE PUBLIC CLASS DEFINITION // ================================ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + '[data-toggle="collapse"][data-target="#' + element.id + '"]') this.transitioning = null if (this.options.parent) { this.$parent = this.getParent() } else { this.addAriaAndCollapsedClass(this.$element, this.$trigger) } if (this.options.toggle) this.toggle() } Collapse.VERSION = '3.3.4' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { toggle: true } Collapse.prototype.dimension = function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return var activesData var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') if (actives && actives.length) { activesData = actives.data('bs.collapse') if (activesData && activesData.transitioning) return } var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return if (actives && actives.length) { Plugin.call(actives, 'hide') activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) .attr('aria-expanded', true) this.$trigger .removeClass('collapsed') .attr('aria-expanded', true) this.transitioning = 1 var complete = function () { this.$element .removeClass('collapsing') .addClass('collapse in')[dimension]('') this.transitioning = 0 this.$element .trigger('shown.bs.collapse') } if (!$.support.transition) return complete.call(this) var scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !this.$element.hasClass('in')) return var startEvent = $.Event('hide.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var dimension = this.dimension() this.$element[dimension](this.$element[dimension]())[0].offsetHeight this.$element .addClass('collapsing') .removeClass('collapse in') .attr('aria-expanded', false) this.$trigger .addClass('collapsed') .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element .removeClass('collapsing') .addClass('collapse') .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } Collapse.prototype.getParent = function () { return $(this.options.parent) .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') .each($.proxy(function (i, element) { var $element = $(element) this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) }, this)) .end() } Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { var isOpen = $element.hasClass('in') $element.attr('aria-expanded', isOpen) $trigger .toggleClass('collapsed', !isOpen) .attr('aria-expanded', isOpen) } function getTargetFromTrigger($trigger) { var href var target = $trigger.attr('data-target') || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 return $(target) } // COLLAPSE PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.collapse $.fn.collapse = Plugin $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { var $this = $(this) if (!$this.attr('data-target')) e.preventDefault() var $target = getTargetFromTrigger($this) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $this.data() Plugin.call($target, option) }) }(jQuery); /* ======================================================================== * Bootstrap: dropdown.js v3.3.4 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.4' Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $('