From b35a132876cb3fc8716ff864c509b1d0367f8362 Mon Sep 17 00:00:00 2001 From: nelsonlaquet Date: Thu, 25 Jul 2013 16:33:04 -0500 Subject: [PATCH] Inital commit --- .gitattributes | 1 + .gitignore | 37 + CONTRIBUTING.md | 3 + app/commands/.gitkeep | 0 app/config/app.php | 184 + app/config/auth.php | 63 + app/config/cache.php | 89 + app/config/compile.php | 18 + app/config/database.php | 124 + app/config/local/.gitignore | 2 + app/config/mail.php | 111 + app/config/packages/.gitkeep | 0 app/config/queue.php | 60 + app/config/session.php | 125 + app/config/testing/cache.php | 20 + app/config/testing/session.php | 21 + app/config/view.php | 31 + app/config/workbench.php | 31 + app/controllers/AccountController.php | 7 + app/controllers/AlbumsController.php | 7 + app/controllers/Api/Web/AuthController.php | 30 + .../Api/Web/TaxonomiesController.php | 17 + app/controllers/Api/Web/TracksController.php | 99 + app/controllers/ApiControllerBase.php | 23 + app/controllers/ArtistsController.php | 7 + app/controllers/ContentController.php | 15 + app/controllers/FavoritesController.php | 15 + app/controllers/HomeController.php | 7 + app/controllers/PlaylistsController.php | 7 + app/controllers/TracksController.php | 7 + app/database/migrations/.gitkeep | 0 .../2013_06_07_003952_create_users_table.php | 57 + .../2013_06_27_015259_create_tracks_table.php | 117 + app/database/production.sqlite | 0 app/database/seeds/.gitkeep | 0 app/database/seeds/DatabaseSeeder.php | 17 + app/filters.php | 79 + app/lang/en/pagination.php | 20 + app/lang/en/reminders.php | 22 + app/lang/en/validation.php | 104 + app/library/Assets.php | 81 + app/library/AudioCache.php | 16 + app/library/CacheBusterAsset.php | 32 + app/library/Helpers.php | 7 + app/library/IpsHasher.php | 29 + app/library/PfmAuth.php | 15 + app/library/PfmValidator.php | 147 + app/models/Commands/CommandBase.php | 29 + app/models/Commands/CommandResponse.php | 49 + app/models/Commands/DeleteTrackCommand.php | 32 + app/models/Commands/EditTrackCommand.php | 67 + app/models/Commands/UploadTrackCommand.php | 84 + app/models/Entities/Genre.php | 7 + app/models/Entities/License.php | 7 + app/models/Entities/Track.php | 44 + app/models/Entities/TrackType.php | 7 + app/models/Entities/User.php | 23 + app/routes.php | 63 + app/start/artisan.php | 13 + app/start/global.php | 95 + app/start/local.php | 3 + app/storage/.gitignore | 1 + app/storage/cache/.gitignore | 2 + app/storage/logs/.gitignore | 2 + app/storage/meta/.gitignore | 2 + app/storage/scripts/.gitignore | 2 + app/storage/sessions/.gitignore | 2 + app/storage/styles/.gitignore | 2 + app/storage/views/.gitignore | 2 + app/tests/ExampleTest.php | 19 + app/tests/TestCase.php | 19 + app/views/albums/index.blade.php | 6 + app/views/artists/index.blade.php | 6 + app/views/auth/login.blade.php | 6 + app/views/auth/register.blade.php | 6 + app/views/emails/auth/reminder.blade.php | 13 + app/views/home/index.blade.php | 6 + app/views/pages/about.blade.php | 5 + app/views/pages/faq.blade.php | 5 + app/views/playlists/index.blade.php | 6 + app/views/shared/_app_layout.blade.php | 96 + app/views/shared/_layout.blade.php | 18 + app/views/shared/null.blade.php | 5 + app/views/tracks/index.blade.php | 6 + artisan | 74 + bootstrap/autoload.php | 75 + bootstrap/compiled.php | 10086 +++++++++ bootstrap/paths.php | 57 + bootstrap/start.php | 72 + composer.json | 32 + composer.lock | 2303 +++ composer.phar | Bin 0 -> 807659 bytes phpunit.xml | 18 + public/.htaccess | 9 + public/asset.php | 66 + public/favicon.ico | 0 public/fonts/FontAwesome.otf | Bin 0 -> 50204 bytes public/fonts/fontawesome-webfont.eot | Bin 0 -> 29360 bytes public/fonts/fontawesome-webfont.svg | 339 + public/fonts/fontawesome-webfont.ttf | Bin 0 -> 64960 bytes public/fonts/fontawesome-webfont.woff | Bin 0 -> 34420 bytes public/images/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes public/images/glyphicons-halflings.png | Bin 0 -> 12799 bytes public/index.php | 62 + public/robots.txt | 2 + public/scripts/app/app.coffee | 120 + .../controllers/account-content-tracks.coffee | 177 + .../app/controllers/account-settings.coffee | 4 + .../app/controllers/application.coffee | 16 + public/scripts/app/controllers/login.coffee | 18 + public/scripts/app/controllers/upload.coffee | 32 + .../scripts/app/directives/eat-click.coffee | 4 + .../app/directives/progress-bar.coffee | 5 + public/scripts/app/directives/uploader.coffee | 22 + public/scripts/app/filters/length.coffee | 3 + public/scripts/app/filters/pfm-date.js | 216 + public/scripts/app/services/auth.coffee | 18 + public/scripts/app/services/taxonomies.coffee | 23 + public/scripts/app/services/upload.coffee | 63 + public/scripts/base/angular-ui-date.js | 121 + public/scripts/base/angular-ui-router.js | 1037 + public/scripts/base/angular.js | 16876 ++++++++++++++++ public/scripts/base/jquery-2.0.2.js | 8842 ++++++++ public/scripts/base/jquery-ui.js | 15003 ++++++++++++++ .../scripts/base/ui-bootstrap-tpls-0.4.0.js | 3165 +++ public/scripts/base/underscore.js | 1227 ++ public/scripts/shared/layout.coffee | 13 + public/styles/account-tracks.less | 310 + public/styles/app.less | 7 + public/styles/base/bootstrap/accordion.less | 34 + public/styles/base/bootstrap/alerts.less | 79 + public/styles/base/bootstrap/bootstrap.less | 64 + public/styles/base/bootstrap/breadcrumbs.less | 24 + .../styles/base/bootstrap/button-groups.less | 229 + public/styles/base/bootstrap/buttons.less | 228 + public/styles/base/bootstrap/carousel.less | 158 + public/styles/base/bootstrap/close.less | 32 + public/styles/base/bootstrap/code.less | 61 + .../base/bootstrap/component-animations.less | 22 + public/styles/base/bootstrap/dropdowns.less | 248 + public/styles/base/bootstrap/forms.less | 690 + public/styles/base/bootstrap/grid.less | 21 + public/styles/base/bootstrap/hero-unit.less | 25 + .../styles/base/bootstrap/labels-badges.less | 84 + public/styles/base/bootstrap/layouts.less | 16 + public/styles/base/bootstrap/media.less | 55 + public/styles/base/bootstrap/mixins.less | 702 + public/styles/base/bootstrap/modals.less | 95 + public/styles/base/bootstrap/navbar.less | 497 + public/styles/base/bootstrap/navs.less | 409 + public/styles/base/bootstrap/pager.less | 43 + public/styles/base/bootstrap/pagination.less | 123 + public/styles/base/bootstrap/popovers.less | 133 + .../styles/base/bootstrap/progress-bars.less | 122 + public/styles/base/bootstrap/reset.less | 216 + .../base/bootstrap/responsive-1200px-min.less | 28 + .../base/bootstrap/responsive-767px-max.less | 193 + .../bootstrap/responsive-768px-979px.less | 19 + .../base/bootstrap/responsive-navbar.less | 189 + .../base/bootstrap/responsive-utilities.less | 59 + public/styles/base/bootstrap/responsive.less | 48 + public/styles/base/bootstrap/scaffolding.less | 53 + public/styles/base/bootstrap/sprites.less | 197 + public/styles/base/bootstrap/tables.less | 244 + public/styles/base/bootstrap/thumbnails.less | 53 + public/styles/base/bootstrap/tooltip.less | 70 + public/styles/base/bootstrap/type.less | 247 + public/styles/base/bootstrap/utilities.less | 30 + public/styles/base/bootstrap/variables.less | 301 + public/styles/base/bootstrap/wells.less | 29 + .../styles/base/font-awesome/bootstrap.less | 78 + public/styles/base/font-awesome/core.less | 132 + public/styles/base/font-awesome/extras.less | 79 + .../base/font-awesome/font-awesome-ie7.less | 413 + .../base/font-awesome/font-awesome.less | 32 + public/styles/base/font-awesome/icons.less | 330 + public/styles/base/font-awesome/mixins.less | 34 + public/styles/base/font-awesome/path.less | 15 + .../styles/base/font-awesome/variables.less | 9 + .../styles/base/images/animated-overlay.gif | Bin 0 -> 1738 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../base/images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes public/styles/base/jquery-ui.css | 1188 ++ public/styles/components.less | 64 + public/styles/home.less | 4 + public/styles/layout.less | 528 + public/styles/mixins.less | 103 + public/templates/account/content/_layout.html | 9 + public/templates/account/content/albums.html | 1 + .../templates/account/content/playlists.html | 1 + public/templates/account/content/tracks.html | 147 + .../templates/account/favorites/_layout.html | 14 + .../templates/account/favorites/albums.html | 1 + .../account/favorites/playlists.html | 1 + .../templates/account/favorites/tracks.html | 1 + public/templates/account/settings.html | 3 + public/templates/albums/index.html | 3 + public/templates/artists/index.html | 3 + public/templates/auth/login.html | 4 + public/templates/auth/register.html | 4 + public/templates/home/index.html | 13 + public/templates/pages/about.html | 11 + public/templates/pages/faq.html | 23 + public/templates/partials/auth/login.html | 27 + public/templates/partials/upload-dialog.html | 19 + public/templates/playlists/index.html | 3 + public/templates/tracks/_layout.html | 14 + public/templates/tracks/index.html | 1 + readme.md | 9 + server.php | 19 + spa.pony.fm.iml | 160 + vendor/autoload.php | 7 + vendor/bin/classpreloader.php | 7 + vendor/bin/classpreloader.php.bat | 3 + vendor/codescale/ffmpeg-php/.gitignore | 7 + .../ffmpeg-php/FFmpegAnimatedGif.php | 292 + .../codescale/ffmpeg-php/FFmpegAutoloader.php | 63 + vendor/codescale/ffmpeg-php/FFmpegFrame.php | 192 + vendor/codescale/ffmpeg-php/FFmpegMovie.php | 765 + vendor/codescale/ffmpeg-php/LICENSE | 26 + vendor/codescale/ffmpeg-php/README.rst | 96 + .../adapter/ffmpeg_animated_gif.php | 32 + .../ffmpeg-php/adapter/ffmpeg_frame.php | 55 + .../ffmpeg-php/adapter/ffmpeg_movie.php | 149 + vendor/codescale/ffmpeg-php/composer.json | 23 + vendor/codescale/ffmpeg-php/package.xml | 150 + .../provider/AbstractOutputProvider.php | 73 + .../provider/FFmpegOutputProvider.php | 60 + .../provider/FFprobeOutputProvider.php | 59 + .../ffmpeg-php/provider/OutputProvider.php | 26 + .../provider/StringOutputProvider.php | 55 + .../ffmpeg-php/test/FFmpegAnimatedGifTest.php | 101 + .../ffmpeg-php/test/FFmpegAutoloaderTest.php | 34 + .../ffmpeg-php/test/FFmpegFrameTest.php | 133 + .../ffmpeg-php/test/FFmpegMovieTest.php | 271 + .../test/adapter/ffmpeg_animated_gif_Test.php | 81 + .../test/adapter/ffmpeg_frame_Test.php | 134 + .../test/adapter/ffmpeg_movie_Test.php | 273 + .../codescale/ffmpeg-php/test/bootstrap.php | 26 + .../codescale/ffmpeg-php/test/data/test.mp4 | Bin 0 -> 1191777 bytes .../codescale/ffmpeg-php/test/data/test.wav | Bin 0 -> 354468 bytes .../codescale/ffmpeg-php/test/data/test1.txt | 1 + .../provider/FFmpegOutputProviderTest.php | 94 + .../provider/FFprobeOutputProviderTest.php | 94 + vendor/composer/ClassLoader.php | 246 + vendor/composer/autoload_classmap.php | 1837 ++ vendor/composer/autoload_namespaces.php | 50 + vendor/composer/autoload_real.php | 48 + vendor/composer/installed.json | 2364 +++ vendor/doctrine/annotations/.gitignore | 1 + vendor/doctrine/annotations/.travis.yml | 8 + vendor/doctrine/annotations/README.md | 9 + vendor/doctrine/annotations/composer.json | 30 + vendor/doctrine/annotations/composer.lock | 129 + .../Common/Annotations/Annotation.php | 79 + .../Annotations/Annotation/Attribute.php | 47 + .../Annotations/Annotation/Attributes.php | 37 + .../Common/Annotations/Annotation/Enum.php | 85 + .../Annotation/IgnoreAnnotation.php | 54 + .../Annotations/Annotation/Required.php | 33 + .../Common/Annotations/Annotation/Target.php | 107 + .../Annotations/AnnotationException.php | 158 + .../Common/Annotations/AnnotationReader.php | 318 + .../Common/Annotations/AnnotationRegistry.php | 139 + .../Common/Annotations/CachedReader.php | 250 + .../Doctrine/Common/Annotations/DocLexer.php | 132 + .../Doctrine/Common/Annotations/DocParser.php | 1041 + .../Common/Annotations/FileCacheReader.php | 269 + .../Common/Annotations/IndexedReader.php | 141 + .../Doctrine/Common/Annotations/PhpParser.php | 89 + .../Doctrine/Common/Annotations/Reader.php | 67 + .../Annotations/SimpleAnnotationReader.php | 157 + .../Common/Annotations/TokenParser.php | 175 + vendor/doctrine/annotations/phpunit.xml.dist | 31 + .../Common/Annotations/AbstractReaderTest.php | 571 + .../Annotations/AnnotationReaderTest.php | 13 + .../Common/Annotations/CachedReaderTest.php | 56 + .../Tests/Common/Annotations/DocLexerTest.php | 137 + .../Common/Annotations/DocParserTest.php | 1264 ++ .../Tests/Common/Annotations/DummyClass.php | 48 + .../Annotations/FileCacheReaderTest.php | 40 + .../Annotation/AnnotWithDefaultValue.php | 10 + .../Fixtures/Annotation/Autoload.php | 10 + .../Annotations/Fixtures/Annotation/Route.php | 11 + .../Fixtures/Annotation/Secure.php | 18 + .../Fixtures/Annotation/Template.php | 14 + .../Fixtures/Annotation/Version.php | 11 + .../Annotations/Fixtures/AnnotationEnum.php | 21 + .../Fixtures/AnnotationEnumInvalid.php | 17 + .../Fixtures/AnnotationEnumLiteral.php | 34 + .../Fixtures/AnnotationEnumLiteralInvalid.php | 31 + .../Fixtures/AnnotationTargetAll.php | 14 + .../Fixtures/AnnotationTargetAnnotation.php | 14 + .../Fixtures/AnnotationTargetClass.php | 15 + .../Fixtures/AnnotationTargetMethod.php | 15 + .../AnnotationTargetPropertyMethod.php | 14 + .../Fixtures/AnnotationWithAttributes.php | 119 + .../Fixtures/AnnotationWithConstants.php | 20 + .../AnnotationWithRequiredAttributes.php | 50 + ...ithRequiredAttributesWithoutContructor.php | 24 + .../AnnotationWithTargetSyntaxError.php | 11 + .../Fixtures/AnnotationWithVarType.php | 62 + .../Tests/Common/Annotations/Fixtures/Api.php | 10 + .../Annotations/Fixtures/ClassDDC1660.php | 30 + .../Fixtures/ClassWithAnnotationEnum.php | 29 + ...assWithAnnotationWithTargetSyntaxError.php | 21 + .../ClassWithAnnotationWithVarType.php | 31 + .../Annotations/Fixtures/ClassWithClosure.php | 52 + .../Fixtures/ClassWithConstants.php | 10 + .../ClassWithFullyQualifiedUseStatements.php | 11 + ...lassWithInvalidAnnotationTargetAtClass.php | 17 + ...assWithInvalidAnnotationTargetAtMethod.php | 20 + ...sWithInvalidAnnotationTargetAtProperty.php | 24 + .../Annotations/Fixtures/ClassWithRequire.php | 15 + .../ClassWithValidAnnotationTarget.php | 41 + .../Annotations/Fixtures/Controller.php | 300 + ...erentNamespacesPerFileWithClassAsFirst.php | 15 + ...ferentNamespacesPerFileWithClassAsLast.php | 15 + ...EqualNamespacesPerFileWithClassAsFirst.php | 13 + .../EqualNamespacesPerFileWithClassAsLast.php | 12 + ...lobalNamespacesPerFileWithClassAsFirst.php | 12 + ...GlobalNamespacesPerFileWithClassAsLast.php | 12 + .../Fixtures/IntefaceWithConstants.php | 10 + .../InvalidAnnotationUsageButIgnoredClass.php | 14 + .../Fixtures/InvalidAnnotationUsageClass.php | 10 + .../Fixtures/MultipleClassesInFile.php | 9 + .../MultipleImportsInUseStatement.php | 10 + .../NamespaceAndClassCommentedOut.php | 20 + .../NamespaceWithClosureDeclaration.php | 12 + .../Fixtures/NamespacedSingleClassLOC1000.php | 1009 + .../Annotations/Fixtures/NoAnnotation.php | 5 + .../Fixtures/NonNamespacedClass.php | 10 + .../Fixtures/SingleClassLOC1000.php | 1006 + .../Annotations/Fixtures/TestInterface.php | 13 + .../Common/Annotations/PerformanceTest.php | 194 + .../Common/Annotations/PhpParserTest.php | 207 + .../SimpleAnnotationReaderTest.php | 97 + .../Common/Annotations/Ticket/DCOM55Test.php | 65 + .../Annotations/Ticket/DCOM58Entity.php | 8 + .../Common/Annotations/Ticket/DCOM58Test.php | 112 + .../Common/Annotations/TopLevelAnnotation.php | 8 + .../tests/Doctrine/Tests/DoctrineTestCase.php | 10 + .../tests/Doctrine/Tests/TestInit.php | 26 + vendor/filp/whoops/.gitignore | 4 + vendor/filp/whoops/.travis.yml | 6 + vendor/filp/whoops/LICENSE.md | 19 + vendor/filp/whoops/README.md | 210 + vendor/filp/whoops/composer.json | 27 + vendor/filp/whoops/composer.lock | 477 + .../whoops/examples/example-ajax-only.php | 46 + vendor/filp/whoops/examples/example-silex.php | 36 + vendor/filp/whoops/examples/example.php | 63 + vendor/filp/whoops/phpunit.xml.dist | 15 + .../src/Whoops/Exception/ErrorException.php | 14 + .../whoops/src/Whoops/Exception/Frame.php | 228 + .../src/Whoops/Exception/FrameCollection.php | 122 + .../whoops/src/Whoops/Exception/Inspector.php | 100 + .../src/Whoops/Handler/CallbackHandler.php | 48 + .../whoops/src/Whoops/Handler/Handler.php | 89 + .../src/Whoops/Handler/HandlerInterface.php | 33 + .../Whoops/Handler/JsonResponseHandler.php | 106 + .../src/Whoops/Handler/PrettyPageHandler.php | 328 + .../Provider/Silex/WhoopsServiceProvider.php | 81 + .../Provider/Zend/ExceptionStrategy.php | 56 + .../src/Whoops/Provider/Zend/Module.php | 106 + .../Provider/Zend/RouteNotFoundStrategy.php | 64 + .../Provider/Zend/module.config.example.php | 20 + .../src/Whoops/Resources/pretty-page.css | 311 + .../src/Whoops/Resources/pretty-template.php | 203 + vendor/filp/whoops/src/Whoops/Run.php | 247 + .../Whoops/Exception/FrameCollectionTest.php | 150 + .../tests/Whoops/Exception/FrameTest.php | 209 + .../tests/Whoops/Exception/InspectorTest.php | 67 + .../Handler/JsonResponseHandlerTest.php | 96 + .../Whoops/Handler/PrettyPageHandlerTest.php | 268 + vendor/filp/whoops/tests/Whoops/RunTest.php | 330 + vendor/filp/whoops/tests/Whoops/TestCase.php | 22 + vendor/filp/whoops/tests/bootstrap.php | 11 + .../tests/fixtures/frame.lines-test.php | 10 + vendor/kriswallsmith/assetic/CHANGELOG-1.0.md | 36 + vendor/kriswallsmith/assetic/CHANGELOG-1.1.md | 49 + vendor/kriswallsmith/assetic/CHANGELOG-1.2.md | 6 + vendor/kriswallsmith/assetic/Gemfile | 5 + vendor/kriswallsmith/assetic/LICENSE | 19 + vendor/kriswallsmith/assetic/README.md | 340 + vendor/kriswallsmith/assetic/composer.json | 81 + vendor/kriswallsmith/assetic/package.json | 13 + .../assetic/src/Assetic/Asset/AssetCache.php | 169 + .../src/Assetic/Asset/AssetCollection.php | 233 + .../Asset/AssetCollectionInterface.php | 59 + .../src/Assetic/Asset/AssetInterface.php | 156 + .../src/Assetic/Asset/AssetReference.php | 133 + .../assetic/src/Assetic/Asset/BaseAsset.php | 172 + .../assetic/src/Assetic/Asset/FileAsset.php | 78 + .../assetic/src/Assetic/Asset/GlobAsset.php | 113 + .../assetic/src/Assetic/Asset/HttpAsset.php | 79 + .../AssetCollectionFilterIterator.php | 84 + .../Iterator/AssetCollectionIterator.php | 110 + .../assetic/src/Assetic/Asset/StringAsset.php | 55 + .../assetic/src/Assetic/AssetManager.php | 89 + .../assetic/src/Assetic/AssetWriter.php | 94 + .../assetic/src/Assetic/Cache/ApcCache.php | 66 + .../assetic/src/Assetic/Cache/ArrayCache.php | 58 + .../src/Assetic/Cache/CacheInterface.php | 53 + .../assetic/src/Assetic/Cache/ConfigCache.php | 123 + .../src/Assetic/Cache/ExpiringCache.php | 60 + .../src/Assetic/Cache/FilesystemCache.php | 65 + .../src/Assetic/Exception/Exception.php | 21 + .../src/Assetic/Exception/FilterException.php | 73 + .../Extension/Twig/AsseticExtension.php | 76 + .../Extension/Twig/AsseticFilterFunction.php | 29 + .../Extension/Twig/AsseticFilterInvoker.php | 59 + .../Assetic/Extension/Twig/AsseticNode.php | 166 + .../Extension/Twig/AsseticTokenParser.php | 153 + .../Extension/Twig/TwigFormulaLoader.php | 99 + .../Assetic/Extension/Twig/TwigResource.php | 54 + .../Assetic/Extension/Twig/ValueContainer.php | 79 + .../src/Assetic/Factory/AssetFactory.php | 425 + .../src/Assetic/Factory/LazyAssetManager.php | 210 + .../Factory/Loader/BasePhpFormulaLoader.php | 159 + .../Factory/Loader/CachedFormulaLoader.php | 68 + .../Factory/Loader/FormulaLoaderInterface.php | 34 + .../Loader/FunctionCallsFormulaLoader.php | 53 + .../Resource/CoalescingDirectoryResource.php | 112 + .../Factory/Resource/DirectoryResource.php | 133 + .../Assetic/Factory/Resource/FileResource.php | 47 + .../Resource/IteratorResourceInterface.php | 21 + .../Factory/Resource/ResourceInterface.php | 43 + .../Factory/Worker/CacheBustingWorker.php | 71 + .../Factory/Worker/EnsureFilterWorker.php | 61 + .../Factory/Worker/WorkerInterface.php | 33 + .../src/Assetic/Filter/BaseCssFilter.php | 54 + .../src/Assetic/Filter/BaseNodeFilter.php | 44 + .../src/Assetic/Filter/BaseProcessFilter.php | 58 + .../src/Assetic/Filter/CallablesFilter.php | 63 + .../src/Assetic/Filter/CoffeeScriptFilter.php | 72 + .../src/Assetic/Filter/CompassFilter.php | 401 + .../src/Assetic/Filter/CssEmbedFilter.php | 145 + .../src/Assetic/Filter/CssImportFilter.php | 108 + .../src/Assetic/Filter/CssMinFilter.php | 74 + .../src/Assetic/Filter/CssRewriteFilter.php | 102 + .../assetic/src/Assetic/Filter/DartFilter.php | 67 + .../Filter/DependencyExtractorInterface.php | 34 + .../Assetic/Filter/EmberPrecompileFilter.php | 83 + .../src/Assetic/Filter/FilterCollection.php | 82 + .../src/Assetic/Filter/FilterInterface.php | 36 + .../GoogleClosure/BaseCompilerFilter.php | 101 + .../GoogleClosure/CompilerApiFilter.php | 132 + .../GoogleClosure/CompilerJarFilter.php | 98 + .../assetic/src/Assetic/Filter/GssFilter.php | 141 + .../src/Assetic/Filter/HandlebarsFilter.php | 102 + .../src/Assetic/Filter/HashableInterface.php | 27 + .../src/Assetic/Filter/JSMinFilter.php | 34 + .../src/Assetic/Filter/JSMinPlusFilter.php | 34 + .../src/Assetic/Filter/JpegoptimFilter.php | 80 + .../src/Assetic/Filter/JpegtranFilter.php | 102 + .../assetic/src/Assetic/Filter/LessFilter.php | 208 + .../src/Assetic/Filter/LessphpFilter.php | 150 + .../src/Assetic/Filter/OptiPngFilter.php | 74 + .../src/Assetic/Filter/PackagerFilter.php | 64 + .../src/Assetic/Filter/PackerFilter.php | 56 + .../src/Assetic/Filter/PhpCssEmbedFilter.php | 55 + .../src/Assetic/Filter/PngoutFilter.php | 127 + .../src/Assetic/Filter/RooleFilter.php | 73 + .../src/Assetic/Filter/Sass/SassFilter.php | 236 + .../src/Assetic/Filter/Sass/ScssFilter.php | 28 + .../src/Assetic/Filter/ScssphpFilter.php | 80 + .../src/Assetic/Filter/SprocketsFilter.php | 154 + .../src/Assetic/Filter/StylusFilter.php | 128 + .../src/Assetic/Filter/TypeScriptFilter.php | 76 + .../src/Assetic/Filter/UglifyCssFilter.php | 119 + .../src/Assetic/Filter/UglifyJs2Filter.php | 135 + .../src/Assetic/Filter/UglifyJsFilter.php | 145 + .../Filter/Yui/BaseCompressorFilter.php | 116 + .../Filter/Yui/CssCompressorFilter.php | 28 + .../Assetic/Filter/Yui/JsCompressorFilter.php | 61 + .../assetic/src/Assetic/FilterManager.php | 64 + .../assetic/src/Assetic/Util/CssUtils.php | 111 + .../assetic/src/Assetic/Util/LessUtils.php | 23 + .../src/Assetic/Util/TraversableString.php | 44 + .../assetic/src/Assetic/Util/VarUtils.php | 82 + .../src/Assetic/ValueSupplierInterface.php | 29 + .../kriswallsmith/assetic/src/functions.php | 121 + vendor/monolog/monolog/CHANGELOG.mdown | 92 + vendor/monolog/monolog/LICENSE | 19 + vendor/monolog/monolog/README.mdown | 230 + vendor/monolog/monolog/composer.json | 39 + vendor/monolog/monolog/doc/extending.md | 76 + vendor/monolog/monolog/doc/sockets.md | 37 + vendor/monolog/monolog/doc/usage.md | 158 + vendor/monolog/monolog/phpunit.xml.dist | 15 + .../Monolog/Formatter/ChromePHPFormatter.php | 79 + .../Monolog/Formatter/FormatterInterface.php | 36 + .../Formatter/GelfMessageFormatter.php | 94 + .../src/Monolog/Formatter/JsonFormatter.php | 38 + .../src/Monolog/Formatter/LineFormatter.php | 102 + .../Monolog/Formatter/LogstashFormatter.php | 98 + .../Monolog/Formatter/NormalizerFormatter.php | 101 + .../Monolog/Formatter/WildfireFormatter.php | 102 + .../src/Monolog/Handler/AbstractHandler.php | 174 + .../Handler/AbstractProcessingHandler.php | 66 + .../src/Monolog/Handler/AmqpHandler.php | 69 + .../src/Monolog/Handler/BufferHandler.php | 98 + .../src/Monolog/Handler/ChromePHPHandler.php | 183 + .../src/Monolog/Handler/CouchDBHandler.php | 72 + .../src/Monolog/Handler/CubeHandler.php | 145 + .../Handler/DoctrineCouchDBHandler.php | 45 + .../ActivationStrategyInterface.php | 28 + .../ErrorLevelActivationStrategy.php | 32 + .../Monolog/Handler/FingersCrossedHandler.php | 113 + .../src/Monolog/Handler/FirePHPHandler.php | 184 + .../src/Monolog/Handler/GelfHandler.php | 66 + .../src/Monolog/Handler/GroupHandler.php | 80 + .../src/Monolog/Handler/HandlerInterface.php | 88 + .../src/Monolog/Handler/MailHandler.php | 55 + .../Handler/MissingExtensionException.php | 22 + .../src/Monolog/Handler/MongoDBHandler.php | 55 + .../Monolog/Handler/NativeMailerHandler.php | 68 + .../src/Monolog/Handler/NullHandler.php | 45 + .../src/Monolog/Handler/PushoverHandler.php | 88 + .../src/Monolog/Handler/RavenHandler.php | 92 + .../src/Monolog/Handler/RedisHandler.php | 58 + .../Monolog/Handler/RotatingFileHandler.php | 126 + .../src/Monolog/Handler/SocketHandler.php | 285 + .../src/Monolog/Handler/StreamHandler.php | 76 + .../Monolog/Handler/SwiftMailerHandler.php | 55 + .../src/Monolog/Handler/SyslogHandler.php | 120 + .../src/Monolog/Handler/TestHandler.php | 140 + .../Monolog/Handler/ZendMonitorHandler.php | 95 + vendor/monolog/monolog/src/Monolog/Logger.php | 564 + .../Processor/IntrospectionProcessor.php | 58 + .../Processor/MemoryPeakUsageProcessor.php | 40 + .../src/Monolog/Processor/MemoryProcessor.php | 50 + .../Processor/MemoryUsageProcessor.php | 40 + .../Monolog/Processor/ProcessIdProcessor.php | 40 + .../Processor/PsrLogMessageProcessor.php | 42 + .../src/Monolog/Processor/UidProcessor.php | 38 + .../src/Monolog/Processor/WebProcessor.php | 62 + .../Formatter/ChromePHPFormatterTest.php | 158 + .../Formatter/GelfMessageFormatterTest.php | 158 + .../Monolog/Formatter/JsonFormatterTest.php | 41 + .../Monolog/Formatter/LineFormatterTest.php | 164 + .../Formatter/LogstashFormatterTest.php | 160 + .../Formatter/NormalizerFormatterTest.php | 160 + .../Formatter/WildfireFormatterTest.php | 111 + .../Functional/Handler/FirePHPHandlerTest.php | 32 + .../Monolog/Handler/AbstractHandlerTest.php | 104 + .../Handler/AbstractProcessingHandlerTest.php | 79 + .../Monolog/Handler/AmqpExchangeMock.php | 38 + .../tests/Monolog/Handler/AmqpHandlerTest.php | 74 + .../Monolog/Handler/BufferHandlerTest.php | 149 + .../Monolog/Handler/ChromePHPHandlerTest.php | 139 + .../Monolog/Handler/CouchDBHandlerTest.php | 41 + .../Handler/DoctrineCouchDBHandlerTest.php | 52 + .../Handler/FingersCrossedHandlerTest.php | 169 + .../Monolog/Handler/FirePHPHandlerTest.php | 94 + .../tests/Monolog/Handler/Fixtures/.gitkeep | 0 .../tests/Monolog/Handler/GelfHandlerTest.php | 94 + .../tests/Monolog/Handler/GelfMocks.php | 25 + .../Monolog/Handler/GroupHandlerTest.php | 89 + .../tests/Monolog/Handler/MailHandlerTest.php | 75 + .../tests/Monolog/Handler/MockRavenClient.php | 26 + .../Monolog/Handler/MongoDBHandlerTest.php | 63 + .../Handler/NativeMailerHandlerTest.php | 43 + .../tests/Monolog/Handler/NullHandlerTest.php | 33 + .../Monolog/Handler/PushoverHandlerTest.php | 108 + .../Monolog/Handler/RavenHandlerTest.php | 95 + .../Monolog/Handler/RedisHandlerTest.php | 71 + .../Handler/RotatingFileHandlerTest.php | 99 + .../Monolog/Handler/SocketHandlerTest.php | 283 + .../Monolog/Handler/StreamHandlerTest.php | 88 + .../Monolog/Handler/SyslogHandlerTest.php | 43 + .../tests/Monolog/Handler/TestHandlerTest.php | 56 + .../Handler/ZendMonitorHandlerTest.php | 69 + .../monolog/tests/Monolog/LoggerTest.php | 409 + .../Processor/IntrospectionProcessorTest.php | 65 + .../MemoryPeakUsageProcessorTest.php | 29 + .../Processor/MemoryUsageProcessorTest.php | 29 + .../Processor/ProcessIdProcessorTest.php | 30 + .../Monolog/Processor/UidProcessorTest.php | 27 + .../Monolog/Processor/WebProcessorTest.php | 68 + .../tests/Monolog/PsrLogCompatTest.php | 47 + .../monolog/tests/Monolog/TestCase.php | 58 + vendor/monolog/monolog/tests/bootstrap.php | 13 + vendor/nesbot/carbon/.gitignore | 2 + vendor/nesbot/carbon/.travis.yml | 7 + vendor/nesbot/carbon/Carbon/Carbon.php | 724 + vendor/nesbot/carbon/Carbon/Tests/AddTest.php | 165 + .../carbon/Carbon/Tests/ComparisonTest.php | 103 + .../carbon/Carbon/Tests/ConstructTest.php | 52 + .../nesbot/carbon/Carbon/Tests/CopyTest.php | 32 + .../Carbon/Tests/CreateFromDateTest.php | 61 + .../Carbon/Tests/CreateFromFormatTest.php | 38 + .../Carbon/Tests/CreateFromTimeTest.php | 62 + .../Carbon/Tests/CreateFromTimestampTest.php | 53 + .../nesbot/carbon/Carbon/Tests/CreateTest.php | 135 + .../nesbot/carbon/Carbon/Tests/DiffTest.php | 438 + .../carbon/Carbon/Tests/FluidSettersTest.php | 110 + .../carbon/Carbon/Tests/GettersTest.php | 200 + .../carbon/Carbon/Tests/InstanceTest.php | 29 + vendor/nesbot/carbon/Carbon/Tests/IsTest.php | 99 + .../nesbot/carbon/Carbon/Tests/IssetTest.php | 29 + .../Tests/NowAndOtherStaticHelpersTest.php | 67 + .../carbon/Carbon/Tests/SettersTest.php | 161 + .../carbon/Carbon/Tests/StartEndOfTest.php | 62 + .../carbon/Carbon/Tests/StringsTest.php | 110 + vendor/nesbot/carbon/Carbon/Tests/SubTest.php | 161 + .../carbon/Carbon/Tests/TestFixture.php | 53 + vendor/nesbot/carbon/LICENSE | 19 + vendor/nesbot/carbon/composer.json | 21 + vendor/nesbot/carbon/composer.lock | 14 + vendor/nesbot/carbon/history.md | 27 + vendor/nesbot/carbon/phpunit.xml.dist | 9 + vendor/nesbot/carbon/readme.md | 647 + vendor/nesbot/carbon/readme.php | 75 + vendor/nesbot/carbon/readme.src.md | 663 + vendor/patchwork/utf8/.gitattributes | 3 + vendor/patchwork/utf8/README.md | 125 + vendor/patchwork/utf8/class/Normalizer.php | 17 + .../utf8/class/Patchwork/PHP/Shim/Iconv.php | 643 + .../utf8/class/Patchwork/PHP/Shim/Intl.php | 145 + .../class/Patchwork/PHP/Shim/Mbstring.php | 336 + .../class/Patchwork/PHP/Shim/Normalizer.php | 295 + .../utf8/class/Patchwork/PHP/Shim/Xml.php | 60 + .../Patchwork/PHP/Shim/charset/from.big5.ser | 1 + .../Patchwork/PHP/Shim/charset/from.cp037.ser | Bin 0 -> 4192 bytes .../PHP/Shim/charset/from.cp1006.ser | Bin 0 -> 4273 bytes .../PHP/Shim/charset/from.cp1026.ser | Bin 0 -> 4192 bytes .../Patchwork/PHP/Shim/charset/from.cp424.ser | Bin 0 -> 3547 bytes .../Patchwork/PHP/Shim/charset/from.cp437.ser | Bin 0 -> 4254 bytes .../Patchwork/PHP/Shim/charset/from.cp500.ser | Bin 0 -> 4192 bytes .../Patchwork/PHP/Shim/charset/from.cp737.ser | Bin 0 -> 4247 bytes .../Patchwork/PHP/Shim/charset/from.cp775.ser | Bin 0 -> 4228 bytes .../Patchwork/PHP/Shim/charset/from.cp850.ser | Bin 0 -> 4222 bytes .../Patchwork/PHP/Shim/charset/from.cp852.ser | Bin 0 -> 4221 bytes .../Patchwork/PHP/Shim/charset/from.cp855.ser | Bin 0 -> 4222 bytes .../Patchwork/PHP/Shim/charset/from.cp856.ser | Bin 0 -> 3525 bytes .../Patchwork/PHP/Shim/charset/from.cp857.ser | Bin 0 -> 4170 bytes .../Patchwork/PHP/Shim/charset/from.cp860.ser | Bin 0 -> 4253 bytes .../Patchwork/PHP/Shim/charset/from.cp861.ser | Bin 0 -> 4254 bytes .../Patchwork/PHP/Shim/charset/from.cp862.ser | Bin 0 -> 4254 bytes .../Patchwork/PHP/Shim/charset/from.cp863.ser | Bin 0 -> 4254 bytes .../Patchwork/PHP/Shim/charset/from.cp864.ser | Bin 0 -> 4180 bytes .../Patchwork/PHP/Shim/charset/from.cp865.ser | Bin 0 -> 4254 bytes .../Patchwork/PHP/Shim/charset/from.cp866.ser | Bin 0 -> 4244 bytes .../Patchwork/PHP/Shim/charset/from.cp869.ser | Bin 0 -> 4071 bytes .../Patchwork/PHP/Shim/charset/from.cp874.ser | Bin 0 -> 3761 bytes .../Patchwork/PHP/Shim/charset/from.cp875.ser | Bin 0 -> 4189 bytes .../Patchwork/PHP/Shim/charset/from.cp932.ser | Bin 0 -> 149785 bytes .../Patchwork/PHP/Shim/charset/from.cp936.ser | Bin 0 -> 415908 bytes .../Patchwork/PHP/Shim/charset/from.cp949.ser | Bin 0 -> 325759 bytes .../Patchwork/PHP/Shim/charset/from.cp950.ser | Bin 0 -> 258514 bytes .../PHP/Shim/charset/from.gsm0338.ser | Bin 0 -> 2228 bytes .../PHP/Shim/charset/from.iso-8859-1.ser | Bin 0 -> 4192 bytes .../PHP/Shim/charset/from.iso-8859-10.ser | Bin 0 -> 4193 bytes .../PHP/Shim/charset/from.iso-8859-11.ser | Bin 0 -> 4143 bytes .../PHP/Shim/charset/from.iso-8859-13.ser | Bin 0 -> 4196 bytes .../PHP/Shim/charset/from.iso-8859-14.ser | Bin 0 -> 4214 bytes .../PHP/Shim/charset/from.iso-8859-15.ser | Bin 0 -> 4193 bytes .../PHP/Shim/charset/from.iso-8859-16.ser | Bin 0 -> 4195 bytes .../PHP/Shim/charset/from.iso-8859-2.ser | Bin 0 -> 4192 bytes .../PHP/Shim/charset/from.iso-8859-3.ser | Bin 0 -> 4073 bytes .../PHP/Shim/charset/from.iso-8859-4.ser | Bin 0 -> 4192 bytes .../PHP/Shim/charset/from.iso-8859-5.ser | Bin 0 -> 4193 bytes .../PHP/Shim/charset/from.iso-8859-6.ser | Bin 0 -> 3427 bytes .../PHP/Shim/charset/from.iso-8859-7.ser | Bin 0 -> 4093 bytes .../PHP/Shim/charset/from.iso-8859-8.ser | Bin 0 -> 3583 bytes .../PHP/Shim/charset/from.iso-8859-9.ser | Bin 0 -> 4192 bytes .../PHP/Shim/charset/from.koi8-r.ser | Bin 0 -> 4248 bytes .../PHP/Shim/charset/from.koi8-u.ser | Bin 0 -> 4240 bytes .../PHP/Shim/charset/from.mazovia.ser | Bin 0 -> 4254 bytes .../PHP/Shim/charset/from.nextstep.ser | Bin 0 -> 4211 bytes .../PHP/Shim/charset/from.stdenc.ser | 1 + .../PHP/Shim/charset/from.symbol.ser | 1 + .../PHP/Shim/charset/from.turkish.ser | Bin 0 -> 4185 bytes .../PHP/Shim/charset/from.us-ascii-quotes.ser | Bin 0 -> 2020 bytes .../PHP/Shim/charset/from.us-ascii.ser | Bin 0 -> 2016 bytes .../PHP/Shim/charset/from.windows-1250.ser | Bin 0 -> 4124 bytes .../PHP/Shim/charset/from.windows-1251.ser | Bin 0 -> 4193 bytes .../PHP/Shim/charset/from.windows-1252.ser | Bin 0 -> 4124 bytes .../PHP/Shim/charset/from.windows-1253.ser | Bin 0 -> 3921 bytes .../PHP/Shim/charset/from.windows-1254.ser | Bin 0 -> 4090 bytes .../PHP/Shim/charset/from.windows-1255.ser | Bin 0 -> 3821 bytes .../PHP/Shim/charset/from.windows-1256.ser | Bin 0 -> 4213 bytes .../PHP/Shim/charset/from.windows-1257.ser | Bin 0 -> 4005 bytes .../PHP/Shim/charset/from.windows-1258.ser | Bin 0 -> 4057 bytes .../PHP/Shim/charset/from.x-mac-ce.ser | Bin 0 -> 4214 bytes .../PHP/Shim/charset/from.x-mac-cyrillic.ser | Bin 0 -> 4212 bytes .../PHP/Shim/charset/from.x-mac-greek.ser | Bin 0 -> 4190 bytes .../PHP/Shim/charset/from.x-mac-icelandic.ser | Bin 0 -> 4201 bytes .../PHP/Shim/charset/from.x-mac-roman.ser | Bin 0 -> 4207 bytes .../PHP/Shim/charset/from.zdingbat.ser | 1 + .../Patchwork/PHP/Shim/charset/to.gsm0338.ser | Bin 0 -> 2459 bytes .../Patchwork/PHP/Shim/charset/to.mazovia.ser | Bin 0 -> 4232 bytes .../Patchwork/PHP/Shim/charset/to.stdenc.ser | 1 + .../Patchwork/PHP/Shim/charset/to.symbol.ser | 1 + .../PHP/Shim/charset/to.zdingbat.ser | 1 + .../Patchwork/PHP/Shim/charset/translit.ser | 1 + .../PHP/Shim/unidata/canonicalComposition.ser | 1 + .../Shim/unidata/canonicalDecomposition.ser | 1 + .../PHP/Shim/unidata/combiningClass.ser | 1 + .../unidata/compatibilityDecomposition.ser | 1 + .../Patchwork/PHP/Shim/unidata/lowerCase.ser | 1 + .../Patchwork/PHP/Shim/unidata/upperCase.ser | 1 + .../patchwork/utf8/class/Patchwork/Utf8.php | 480 + .../utf8/class/Patchwork/Utf8/Bootup.php | 223 + .../class/Patchwork/Utf8/Bootup/iconv.php | 48 + .../utf8/class/Patchwork/Utf8/Bootup/intl.php | 28 + .../class/Patchwork/Utf8/Bootup/mbstring.php | 40 + .../Patchwork/Utf8/Bootup/utf8_encode.php | 14 + .../Patchwork/Utf8/data/caseFolding_full.ser | 1 + .../Patchwork/Utf8/data/translit_extra.ser | 1 + vendor/patchwork/utf8/composer.json | 24 + vendor/psr/log/.gitignore | 1 + vendor/psr/log/LICENSE | 19 + vendor/psr/log/Psr/Log/AbstractLogger.php | 120 + .../log/Psr/Log/InvalidArgumentException.php | 7 + vendor/psr/log/Psr/Log/LogLevel.php | 18 + .../psr/log/Psr/Log/LoggerAwareInterface.php | 17 + vendor/psr/log/Psr/Log/LoggerAwareTrait.php | 22 + vendor/psr/log/Psr/Log/LoggerInterface.php | 114 + vendor/psr/log/Psr/Log/LoggerTrait.php | 131 + vendor/psr/log/Psr/Log/NullLogger.php | 27 + .../log/Psr/Log/Test/LoggerInterfaceTest.php | 116 + vendor/psr/log/README.md | 45 + vendor/psr/log/composer.json | 17 + vendor/swiftmailer/swiftmailer/.gitignore | 7 + vendor/swiftmailer/swiftmailer/CHANGES | 121 + vendor/swiftmailer/swiftmailer/LICENSE | 165 + vendor/swiftmailer/swiftmailer/README | 16 + vendor/swiftmailer/swiftmailer/README.git | 67 + vendor/swiftmailer/swiftmailer/VERSION | 1 + vendor/swiftmailer/swiftmailer/build.xml | 112 + vendor/swiftmailer/swiftmailer/composer.json | 28 + .../swiftmailer/create_pear_package.php | 42 + .../swiftmailer/swiftmailer/doc/headers.rst | 742 + .../swiftmailer/doc/help-resources.rst | 44 + .../swiftmailer/doc/including-the-files.rst | 46 + vendor/swiftmailer/swiftmailer/doc/index.rst | 16 + .../swiftmailer/doc/installing.rst | 200 + .../swiftmailer/doc/introduction.rst | 135 + .../swiftmailer/swiftmailer/doc/japanese.rst | 22 + .../swiftmailer/swiftmailer/doc/messages.rst | 1046 + .../swiftmailer/swiftmailer/doc/overview.rst | 161 + .../swiftmailer/swiftmailer/doc/plugins.rst | 385 + .../swiftmailer/swiftmailer/doc/sending.rst | 592 + .../swiftmailer/doc/uml/Encoders.graffle | Bin 0 -> 3503 bytes .../swiftmailer/doc/uml/Mime.graffle | Bin 0 -> 5575 bytes .../swiftmailer/doc/uml/Transports.graffle | Bin 0 -> 3061 bytes .../swiftmailer/lib/classes/Swift.php | 81 + .../lib/classes/Swift/Attachment.php | 73 + .../AbstractFilterableInputStream.php | 183 + .../Swift/ByteStream/ArrayByteStream.php | 186 + .../Swift/ByteStream/FileByteStream.php | 225 + .../ByteStream/TemporaryFileByteStream.php | 44 + .../lib/classes/Swift/CharacterReader.php | 69 + .../GenericFixedWidthReader.php | 99 + .../Swift/CharacterReader/UsAsciiReader.php | 85 + .../Swift/CharacterReader/Utf8Reader.php | 181 + .../classes/Swift/CharacterReaderFactory.php | 28 + .../SimpleCharacterReaderFactory.php | 126 + .../lib/classes/Swift/CharacterStream.php | 91 + .../CharacterStream/ArrayCharacterStream.php | 296 + .../CharacterStream/NgCharacterStream.php | 277 + .../lib/classes/Swift/ConfigurableSpool.php | 64 + .../lib/classes/Swift/DependencyContainer.php | 373 + .../lib/classes/Swift/DependencyException.php | 28 + .../lib/classes/Swift/EmbeddedFile.php | 71 + .../swiftmailer/lib/classes/Swift/Encoder.php | 29 + .../classes/Swift/Encoder/Base64Encoder.php | 60 + .../lib/classes/Swift/Encoder/QpEncoder.php | 286 + .../classes/Swift/Encoder/Rfc2231Encoder.php | 86 + .../lib/classes/Swift/Encoding.php | 66 + .../lib/classes/Swift/Events/CommandEvent.php | 67 + .../classes/Swift/Events/CommandListener.php | 26 + .../lib/classes/Swift/Events/Event.php | 40 + .../classes/Swift/Events/EventDispatcher.php | 85 + .../classes/Swift/Events/EventListener.php | 20 + .../lib/classes/Swift/Events/EventObject.php | 65 + .../classes/Swift/Events/ResponseEvent.php | 68 + .../classes/Swift/Events/ResponseListener.php | 26 + .../lib/classes/Swift/Events/SendEvent.php | 128 + .../lib/classes/Swift/Events/SendListener.php | 33 + .../Swift/Events/SimpleEventDispatcher.php | 161 + .../Swift/Events/TransportChangeEvent.php | 29 + .../Swift/Events/TransportChangeListener.php | 47 + .../Swift/Events/TransportExceptionEvent.php | 48 + .../Events/TransportExceptionListener.php | 26 + .../lib/classes/Swift/FailoverTransport.php | 47 + .../lib/classes/Swift/FileSpool.php | 201 + .../lib/classes/Swift/FileStream.php | 26 + .../lib/classes/Swift/Filterable.php | 33 + .../swiftmailer/lib/classes/Swift/Image.php | 63 + .../lib/classes/Swift/InputByteStream.php | 77 + .../lib/classes/Swift/IoException.php | 28 + .../lib/classes/Swift/KeyCache.php | 107 + .../classes/Swift/KeyCache/ArrayKeyCache.php | 210 + .../classes/Swift/KeyCache/DiskKeyCache.php | 328 + .../Swift/KeyCache/KeyCacheInputStream.php | 53 + .../classes/Swift/KeyCache/NullKeyCache.php | 117 + .../KeyCache/SimpleKeyCacheInputStream.php | 129 + .../classes/Swift/LoadBalancedTransport.php | 47 + .../lib/classes/Swift/MailTransport.php | 47 + .../swiftmailer/lib/classes/Swift/Mailer.php | 115 + .../Swift/Mailer/ArrayRecipientIterator.php | 57 + .../Swift/Mailer/RecipientIterator.php | 34 + .../lib/classes/Swift/MemorySpool.php | 84 + .../swiftmailer/lib/classes/Swift/Message.php | 85 + .../lib/classes/Swift/Mime/Attachment.php | 155 + .../classes/Swift/Mime/CharsetObserver.php | 26 + .../lib/classes/Swift/Mime/ContentEncoder.php | 36 + .../ContentEncoder/Base64ContentEncoder.php | 69 + .../ContentEncoder/NativeQpContentEncoder.php | 125 + .../ContentEncoder/PlainContentEncoder.php | 167 + .../Mime/ContentEncoder/QpContentEncoder.php | 125 + .../ContentEncoder/QpContentEncoderProxy.php | 90 + .../Mime/ContentEncoder/RawContentEncoder.php | 65 + .../lib/classes/Swift/Mime/EmbeddedFile.php | 47 + .../classes/Swift/Mime/EncodingObserver.php | 26 + .../lib/classes/Swift/Mime/Grammar.php | 178 + .../lib/classes/Swift/Mime/Header.php | 95 + .../lib/classes/Swift/Mime/HeaderEncoder.php | 26 + .../HeaderEncoder/Base64HeaderEncoder.php | 57 + .../Mime/HeaderEncoder/QpHeaderEncoder.php | 67 + .../lib/classes/Swift/Mime/HeaderFactory.php | 80 + .../lib/classes/Swift/Mime/HeaderSet.php | 171 + .../Swift/Mime/Headers/AbstractHeader.php | 504 + .../classes/Swift/Mime/Headers/DateHeader.php | 127 + .../Mime/Headers/IdentificationHeader.php | 183 + .../Swift/Mime/Headers/MailboxHeader.php | 358 + .../Mime/Headers/ParameterizedHeader.php | 265 + .../classes/Swift/Mime/Headers/PathHeader.php | 146 + .../Swift/Mime/Headers/UnstructuredHeader.php | 114 + .../lib/classes/Swift/Mime/Message.php | 225 + .../lib/classes/Swift/Mime/MimeEntity.php | 117 + .../lib/classes/Swift/Mime/MimePart.php | 218 + .../Swift/Mime/ParameterizedHeader.php | 36 + .../Swift/Mime/SimpleHeaderFactory.php | 192 + .../classes/Swift/Mime/SimpleHeaderSet.php | 387 + .../lib/classes/Swift/Mime/SimpleMessage.php | 655 + .../classes/Swift/Mime/SimpleMimeEntity.php | 857 + .../lib/classes/Swift/MimePart.php | 61 + .../lib/classes/Swift/NullTransport.php | 40 + .../lib/classes/Swift/OutputByteStream.php | 48 + .../classes/Swift/Plugins/AntiFloodPlugin.php | 143 + .../Swift/Plugins/BandwidthMonitorPlugin.php | 166 + .../Swift/Plugins/Decorator/Replacements.php | 33 + .../classes/Swift/Plugins/DecoratorPlugin.php | 211 + .../Swift/Plugins/ImpersonatePlugin.php | 70 + .../lib/classes/Swift/Plugins/Logger.php | 38 + .../classes/Swift/Plugins/LoggerPlugin.php | 143 + .../Swift/Plugins/Loggers/ArrayLogger.php | 74 + .../Swift/Plugins/Loggers/EchoLogger.php | 60 + .../classes/Swift/Plugins/MessageLogger.php | 77 + .../Swift/Plugins/Pop/Pop3Connection.php | 33 + .../Swift/Plugins/Pop/Pop3Exception.php | 29 + .../Swift/Plugins/PopBeforeSmtpPlugin.php | 278 + .../Swift/Plugins/RedirectingPlugin.php | 204 + .../lib/classes/Swift/Plugins/Reporter.php | 34 + .../classes/Swift/Plugins/ReporterPlugin.php | 75 + .../Swift/Plugins/Reporters/HitReporter.php | 61 + .../Swift/Plugins/Reporters/HtmlReporter.php | 41 + .../lib/classes/Swift/Plugins/Sleeper.php | 26 + .../classes/Swift/Plugins/ThrottlerPlugin.php | 204 + .../lib/classes/Swift/Plugins/Timer.php | 26 + .../lib/classes/Swift/Preferences.php | 104 + .../Swift/ReplacementFilterFactory.php | 28 + .../classes/Swift/RfcComplianceException.php | 28 + .../lib/classes/Swift/SendmailTransport.php | 47 + .../lib/classes/Swift/SignedMessage.php | 165 + .../swiftmailer/lib/classes/Swift/Signer.php | 22 + .../lib/classes/Swift/Signers/BodySigner.php | 35 + .../lib/classes/Swift/Signers/DKIMSigner.php | 669 + .../classes/Swift/Signers/DomainKeySigner.php | 505 + .../classes/Swift/Signers/HeaderSigner.php | 67 + .../lib/classes/Swift/Signers/SMimeSigner.php | 430 + .../lib/classes/Swift/SmtpTransport.php | 53 + .../swiftmailer/lib/classes/Swift/Spool.php | 54 + .../lib/classes/Swift/SpoolTransport.php | 48 + .../lib/classes/Swift/StreamFilter.php | 36 + .../ByteArrayReplacementFilter.php | 171 + .../StreamFilters/StringReplacementFilter.php | 67 + .../StringReplacementFilterFactory.php | 46 + .../lib/classes/Swift/SwiftException.php | 28 + .../lib/classes/Swift/Transport.php | 56 + .../Swift/Transport/AbstractSmtpTransport.php | 502 + .../Esmtp/Auth/CramMd5Authenticator.php | 83 + .../Esmtp/Auth/LoginAuthenticator.php | 53 + .../Esmtp/Auth/PlainAuthenticator.php | 52 + .../Swift/Transport/Esmtp/AuthHandler.php | 268 + .../Swift/Transport/Esmtp/Authenticator.php | 37 + .../classes/Swift/Transport/EsmtpHandler.php | 88 + .../Swift/Transport/EsmtpTransport.php | 393 + .../Swift/Transport/FailoverTransport.php | 90 + .../lib/classes/Swift/Transport/IoBuffer.php | 69 + .../Swift/Transport/LoadBalancedTransport.php | 171 + .../classes/Swift/Transport/MailInvoker.php | 34 + .../classes/Swift/Transport/MailTransport.php | 231 + .../classes/Swift/Transport/NullTransport.php | 88 + .../Swift/Transport/SendmailTransport.php | 163 + .../Swift/Transport/SimpleMailInvoker.php | 41 + .../lib/classes/Swift/Transport/SmtpAgent.php | 38 + .../Swift/Transport/SpoolTransport.php | 118 + .../classes/Swift/Transport/StreamBuffer.php | 315 + .../lib/classes/Swift/TransportException.php | 29 + .../lib/classes/Swift/Validate.php | 43 + .../lib/dependency_maps/cache_deps.php | 23 + .../lib/dependency_maps/message_deps.php | 9 + .../lib/dependency_maps/mime_deps.php | 123 + .../lib/dependency_maps/transport_deps.php | 68 + .../swiftmailer/lib/mime_types.php | 76 + .../swiftmailer/lib/preferences.php | 35 + .../swiftmailer/lib/swift_init.php | 28 + .../swiftmailer/lib/swift_required.php | 32 + .../swiftmailer/lib/swift_required_pear.php | 32 + vendor/swiftmailer/swiftmailer/notes/APPS | 15 + vendor/swiftmailer/swiftmailer/notes/CHARSETS | 46 + .../swiftmailer/swiftmailer/notes/message.xml | 22 + .../swiftmailer/notes/rfc/rfc0821.txt | 4050 ++++ .../swiftmailer/notes/rfc/rfc0822.txt | 2901 +++ .../swiftmailer/notes/rfc/rfc1341.txt | 5265 +++++ .../swiftmailer/notes/rfc/rfc1521.txt | 4539 +++++ .../swiftmailer/notes/rfc/rfc1854.txt | 395 + .../swiftmailer/notes/rfc/rfc2015.txt | 450 + .../swiftmailer/notes/rfc/rfc2045.txt | 1739 ++ .../swiftmailer/notes/rfc/rfc2046.txt | 2467 +++ .../swiftmailer/notes/rfc/rfc2047.txt | 843 + .../swiftmailer/notes/rfc/rfc2048.txt | 1180 ++ .../swiftmailer/notes/rfc/rfc2049.txt | 1347 ++ .../swiftmailer/notes/rfc/rfc2183.txt | 675 + .../swiftmailer/notes/rfc/rfc2222.txt | 899 + .../swiftmailer/notes/rfc/rfc2231.txt | 563 + .../swiftmailer/notes/rfc/rfc2234.txt | 787 + .../swiftmailer/notes/rfc/rfc2440.txt | 3642 ++++ .../swiftmailer/notes/rfc/rfc2487.txt | 451 + .../swiftmailer/notes/rfc/rfc2554.txt | 619 + .../swiftmailer/notes/rfc/rfc2821.txt | 4427 ++++ .../swiftmailer/notes/rfc/rfc2822.txt | 2859 +++ .../swiftmailer/notes/rfc/rfc3156.txt | 842 + .../swiftmailer/notes/rfc/rfc3676.txt | 1123 + .../swiftmailer/notes/rfc/rfc4505.txt | 507 + .../swiftmailer/notes/rfc/rfc4616.txt | 619 + .../swiftmailer/notes/rfc/rfc4870.txt | 2298 +++ .../swiftmailer/notes/rfc/rfc4871.txt | 3978 ++++ .../swiftmailer/notes/rfc/rfc4880.txt | 5042 +++++ .../swiftmailer/notes/rfc/rfc4954.txt | 1123 + .../swiftmailer/notes/rfc/rfc5751.txt | 2523 +++ .../swiftmailer/notes/rfc/whats_where.txt | 90 + vendor/swiftmailer/swiftmailer/notes/smtp.txt | 48 + .../swiftmailer/swiftmailer/package.xml.tpl | 73 + .../swiftmailer/test-suite/CHANGES | 7 + .../swiftmailer/test-suite/LICENSE | 165 + .../swiftmailer/swiftmailer/test-suite/README | 159 + .../swiftmailer/test-suite/config.php | 68 + .../swiftmailer/test-suite/index.php | 40 + .../test-suite/lib/Sweety/Reporter.php | 69 + .../lib/Sweety/Reporter/CliReporter.php | 203 + .../Sweety/Reporter/CliTestCaseReporter.php | 160 + .../lib/Sweety/Reporter/HtmlReporter.php | 174 + .../Sweety/Reporter/HtmlTestCaseReporter.php | 174 + .../test-suite/lib/Sweety/Runner.php | 56 + .../lib/Sweety/Runner/AbstractTestRunner.php | 365 + .../lib/Sweety/Runner/CliRunner.php | 128 + .../lib/Sweety/Runner/HtmlRunner.php | 160 + .../test-suite/lib/Sweety/TestLocator.php | 25 + .../Sweety/TestLocator/PearStyleLocator.php | 71 + .../HELP_MY_TESTS_DONT_WORK_ANYMORE | 383 + .../test-suite/lib/simpletest/LICENSE | 502 + .../test-suite/lib/simpletest/README | 108 + .../test-suite/lib/simpletest/TODO.xml | 176 + .../test-suite/lib/simpletest/VERSION | 1 + .../lib/simpletest/authentication.php | 237 + .../test-suite/lib/simpletest/autorun.php | 97 + .../test-suite/lib/simpletest/browser.php | 1094 + .../test-suite/lib/simpletest/collector.php | 122 + .../lib/simpletest/compatibility.php | 166 + .../test-suite/lib/simpletest/cookies.php | 380 + .../lib/simpletest/default_reporter.php | 163 + .../test-suite/lib/simpletest/detached.php | 96 + .../lib/simpletest/docs/en/docs.css | 121 + .../lib/simpletest/docs/fr/docs.css | 84 + .../lib/simpletest/docs/lastcraft/README | 1 + .../lib/simpletest/docs/onpk/README | 1 + .../test-suite/lib/simpletest/docs/pkg/README | 1 + .../lib/simpletest/docs/simpletest.org/README | 1 + .../docs/simpletest.org/favicon.ico | Bin 0 -> 1150 bytes .../images/book-domain-driven-design.jpg | Bin 0 -> 6599 bytes .../book-guide-to-php-design-patterns.jpg | Bin 0 -> 5657 bytes ...nthology-object-oriented-php-solutions.jpg | Bin 0 -> 43265 bytes .../docs/simpletest.org/images/quote.png | Bin 0 -> 620 bytes .../images/simpletest-contribute.png | Bin 0 -> 4299 bytes .../images/simpletest-download.png | Bin 0 -> 4946 bytes .../images/simpletest-external-bottom.png | Bin 0 -> 533 bytes .../images/simpletest-external-middle.png | Bin 0 -> 217 bytes .../images/simpletest-external-top.png | Bin 0 -> 1085 bytes .../images/simpletest-internal-bottom.png | Bin 0 -> 516 bytes .../images/simpletest-internal-middle.png | Bin 0 -> 237 bytes .../images/simpletest-internal-top.png | Bin 0 -> 1158 bytes .../simpletest.org/images/simpletest-logo.png | Bin 0 -> 6892 bytes .../images/simpletest-start-testing.png | Bin 0 -> 5952 bytes .../images/simpletest-support.png | Bin 0 -> 5525 bytes .../simpletest.org/images/test-in-cli.png | Bin 0 -> 46023 bytes .../images/test-with-1-fail.png | Bin 0 -> 25562 bytes .../images/test-with-1-pass.png | Bin 0 -> 10213 bytes .../simpletest/docs/simpletest.org/index.html | 192 + .../simpletest.org/js/jquery-1.2.1.pack.js | 11 + .../js/jquery-speakers_coaches_consultants.js | 30 + .../simpletest.org/js/jquery.heartbeat.js | 9 + .../simpletest.org/js/jquery.sparkline.js | 76 + .../docs/simpletest.org/simpletest.css | 43 + .../docs/simpletest.org/views/heartbeat.php | 174 + .../simpletest.org/views/photos_stream.php | 37 + .../lib/simpletest/docs/source/en/about.xml | 112 + .../en/authentication_documentation.xml | 337 + .../docs/source/en/books_website.xml | 73 + .../source/en/boundary_classes_tutorial.xml | 405 + .../docs/source/en/browser_documentation.xml | 281 + .../simpletest/docs/source/en/changelog.xml | 188 + .../docs/source/en/coding_standards.xml | 135 + .../source/en/display_subclass_tutorial.xml | 267 + .../docs/source/en/download_website.xml | 106 + .../source/en/expectation_documentation.xml | 383 + .../source/en/experimental_dom_tester.xml | 231 + .../docs/source/en/experimental_intro.xml | 54 + .../docs/source/en/experimental_recorder.xml | 170 + .../docs/source/en/extension_eclipse.xml | 279 + .../docs/source/en/first_test_tutorial.xml | 436 + .../source/en/form_testing_documentation.xml | 304 + .../docs/source/en/gain_control_tutorial.xml | 290 + .../source/en/group_test_documentation.xml | 332 + .../docs/source/en/group_test_tutorial.xml | 239 + .../simpletest/docs/source/en/heartbeat.xml | 57 + .../lib/simpletest/docs/source/en/ideas.xml | 188 + .../source/en/improving_design_tutorial.xml | 195 + .../lib/simpletest/docs/source/en/index.xml | 220 + .../lib/simpletest/docs/source/en/intro.xml | 54 + .../source/en/mock_objects_documentation.xml | 708 + .../docs/source/en/mock_objects_tutorial.xml | 359 + .../simpletest/docs/source/en/overview.xml | 460 + .../source/en/partial_mocks_documentation.xml | 402 + .../docs/source/en/photos_stream.xml | 51 + .../docs/source/en/reporter_documentation.xml | 471 + .../simpletest/docs/source/en/screencasts.xml | 76 + .../simpletest/docs/source/en/simple_test.xml | 524 + .../source/en/softwares_using_simpletest.xml | 164 + .../en/speakers_coaches_consultancy.xml | 94 + .../docs/source/en/subclass_tutorial.xml | 255 + .../docs/source/en/support_website.xml | 59 + .../source/en/unit_test_documentation.xml | 313 + .../source/en/web_tester_documentation.xml | 379 + .../docs/source/en/writing_extensions.xml | 128 + .../fr/authentication_documentation.xml | 307 + .../docs/source/fr/books_website.xml | 77 + .../source/fr/boundary_classes_tutorial.xml | 410 + .../docs/source/fr/browser_documentation.xml | 259 + .../source/fr/display_subclass_tutorial.xml | 282 + .../docs/source/fr/download_website.xml | 110 + .../source/fr/expectation_documentation.xml | 308 + .../docs/source/fr/extension_eclipse.xml | 292 + .../docs/source/fr/first_test_tutorial.xml | 462 + .../source/fr/form_testing_documentation.xml | 308 + .../docs/source/fr/gain_control_tutorial.xml | 324 + .../source/fr/group_test_documentation.xml | 353 + .../docs/source/fr/group_test_tutorial.xml | 247 + .../source/fr/improving_design_tutorial.xml | 218 + .../lib/simpletest/docs/source/fr/index.xml | 69 + .../lib/simpletest/docs/source/fr/intro.xml | 39 + .../fr/logiciels_utilisant_simpletest.xml | 121 + .../source/fr/mock_objects_documentation.xml | 729 + .../docs/source/fr/mock_objects_tutorial.xml | 374 + .../simpletest/docs/source/fr/overview.xml | 297 + .../source/fr/partial_mocks_documentation.xml | 419 + .../docs/source/fr/reporter_documentation.xml | 488 + .../simpletest/docs/source/fr/simple_test.xml | 556 + .../docs/source/fr/subclass_tutorial.xml | 261 + .../docs/source/fr/support_website.xml | 60 + .../source/fr/unit_test_documentation.xml | 330 + .../source/fr/web_tester_documentation.xml | 377 + .../test-suite/lib/simpletest/dumper.php | 359 + .../test-suite/lib/simpletest/eclipse.php | 307 + .../test-suite/lib/simpletest/encoding.php | 552 + .../test-suite/lib/simpletest/errors.php | 257 + .../test-suite/lib/simpletest/exceptions.php | 198 + .../test-suite/lib/simpletest/expectation.php | 901 + .../extensions/colortext_reporter.php | 86 + .../lib/simpletest/extensions/css/webunit.css | 78 + .../lib/simpletest/extensions/dom_tester.php | 117 + .../extensions/dom_tester/css_selector.php | 539 + .../dom_tester/test/dom_tester_doc_test.php | 24 + .../dom_tester/test/dom_tester_test.php | 223 + .../test/support/child_adjacent.html | 24 + .../dom_tester/test/support/dom_tester.html | 59 + .../lib/simpletest/extensions/img/wait.gif | Bin 0 -> 1426 bytes .../extensions/js/tests/TestOfWebunit.js.html | 34 + .../lib/simpletest/extensions/js/webunit.js | 194 + .../lib/simpletest/extensions/js/x.js | 423 + .../extensions/junit_xml_reporter.php | 118 + .../simpletest/extensions/pear_test_case.php | 196 + .../lib/simpletest/extensions/recorder.php | 62 + .../extensions/recorder/test/sample.php | 14 + .../extensions/recorder/test/test.php | 28 + .../simpletest/extensions/selenese_tester.php | 393 + .../lib/simpletest/extensions/selenium.php | 136 + .../extensions/selenium/remote-control.php | 128 + .../selenium/test/remote-control_test.php | 36 + .../lib/simpletest/extensions/testdox.php | 55 + .../simpletest/extensions/testdox/test.php | 107 + .../extensions/treemap_reporter.php | 126 + .../extensions/treemap_reporter/jquery.php | 75 + .../test/treemap_node_test.php | 69 + .../treemap_reporter/treemap_recorder.php | 310 + .../extensions/webunit_reporter.php | 285 + .../test-suite/lib/simpletest/form.php | 355 + .../test-suite/lib/simpletest/frames.php | 592 + .../test-suite/lib/simpletest/http.php | 628 + .../test-suite/lib/simpletest/invoker.php | 139 + .../lib/simpletest/mock_objects.php | 1630 ++ .../test-suite/lib/simpletest/packages/README | 26 + .../lib/simpletest/packages/build_tarball.sh | 132 + .../lib/simpletest/packages/bundled_docs.xslt | 246 + .../lib/simpletest/packages/bundled_map.xml | 14 + .../lib/simpletest/packages/extension.xml | 40 + .../simpletest/packages/generate_package.php | 54 + .../lib/simpletest/packages/lastcraft.xslt | 302 + .../simpletest/packages/make_bundled_docs.sh | 25 + .../packages/make_bundled_docs_with_xalan.sh | 26 + .../packages/make_lastcraft_docs.sh | 2 + .../simpletest/packages/make_phpdoc_docs.sh | 42 + .../lib/simpletest/packages/onpk/map_onpk.xml | 26 + .../lib/simpletest/packages/onpk/onpk.xslt | 227 + .../packages/onpk/transform_all_onpk.php | 32 + .../lib/simpletest/packages/package.xml | 710 + .../packages/pear_package_create.php | 170 + .../lib/simpletest/packages/phpdoc_docs.xslt | 127 + .../lib/simpletest/packages/simpletest.ini | 92 + .../packages/simpletest.org/index.php | 54 + .../packages/simpletest.org/integration.php | 41 + .../packages/simpletest.org/map.xml | 87 + .../packages/simpletest.org/package.php | 440 + .../packages/simpletest.org/template.html | 59 + .../test/package/content_without_section.xml | 91 + .../test/package/en/synchronisation.xml | 92 + .../test/package/fr/no-synchronisation.xml | 91 + .../test/package/fr/synchronisation.xml | 93 + .../test/package/here_download.xml | 91 + .../test/package/here_overview.xml | 91 + .../test/package/here_simpletest.xml | 91 + .../test/package/here_start_testing.xml | 91 + .../test/package/here_support.xml | 91 + .../test/package/here_unit-tester.xml | 91 + .../simpletest.org/test/package/map.xml | 20 + .../test/package/one_section_changelogged.xml | 65 + .../test/package/one_section_milestoned.xml | 95 + .../package/one_section_with_autorum_php.xml | 87 + .../package/one_section_with_php_code.xml | 93 + .../simpletest.org/test/package_test.php | 150 + .../lib/simpletest/packages/site_map.xml | 33 + .../packages/transform_all_lastcraft.php | 17 + .../test-suite/lib/simpletest/page.php | 979 + .../test-suite/lib/simpletest/parser.php | 760 + .../lib/simpletest/reflection_php4.php | 136 + .../lib/simpletest/reflection_php5.php | 386 + .../test-suite/lib/simpletest/remote.php | 115 + .../test-suite/lib/simpletest/reporter.php | 446 + .../test-suite/lib/simpletest/scorer.php | 862 + .../test-suite/lib/simpletest/selector.php | 141 + .../lib/simpletest/shell_tester.php | 330 + .../test-suite/lib/simpletest/simpletest.php | 396 + .../test-suite/lib/simpletest/socket.php | 308 + .../test-suite/lib/simpletest/tag.php | 1418 ++ .../lib/simpletest/test/acceptance_test.php | 1653 ++ .../lib/simpletest/test/adapter_test.php | 50 + .../lib/simpletest/test/all_tests.php | 13 + .../simpletest/test/authentication_test.php | 145 + .../lib/simpletest/test/autorun_test.php | 13 + .../lib/simpletest/test/bad_test_suite.php | 10 + .../lib/simpletest/test/browser_test.php | 774 + .../lib/simpletest/test/collector_test.php | 50 + .../lib/simpletest/test/command_line_test.php | 40 + .../simpletest/test/compatibility_test.php | 87 + .../lib/simpletest/test/cookies_test.php | 227 + .../lib/simpletest/test/detached_test.php | 15 + .../lib/simpletest/test/dumper_test.php | 88 + .../lib/simpletest/test/eclipse_test.php | 32 + .../lib/simpletest/test/encoding_test.php | 213 + .../lib/simpletest/test/errors_test.php | 229 + .../lib/simpletest/test/exceptions_test.php | 153 + .../lib/simpletest/test/expectation_test.php | 223 + .../lib/simpletest/test/extensions_tests.php | 23 + .../lib/simpletest/test/form_test.php | 324 + .../lib/simpletest/test/frames_test.php | 549 + .../lib/simpletest/test/http_test.php | 424 + .../lib/simpletest/test/interfaces_test.php | 137 + .../test/interfaces_test_php5_1.php | 14 + .../lib/simpletest/test/live_test.php | 47 + .../lib/simpletest/test/mock_objects_test.php | 1012 + .../lib/simpletest/test/page_test.php | 898 + .../lib/simpletest/test/parse_error_test.php | 9 + .../lib/simpletest/test/parser_test.php | 551 + .../simpletest/test/reflection_php4_test.php | 61 + .../simpletest/test/reflection_php5_test.php | 263 + .../lib/simpletest/test/remote_test.php | 19 + .../lib/simpletest/test/shell_test.php | 38 + .../lib/simpletest/test/shell_tester_test.php | 42 + .../lib/simpletest/test/simpletest_test.php | 58 + .../lib/simpletest/test/site/.htaccess | 2 + .../lib/simpletest/test/site/1.html | 6 + .../lib/simpletest/test/site/2.html | 6 + .../lib/simpletest/test/site/3.html | 6 + .../test/site/base_change_redirect.php | 6 + .../test/site/base_tag/base_link.html | 9 + .../simpletest/test/site/base_tag/form.html | 50 + .../test/site/base_tag/frameset.html | 9 + .../site/base_tag/frameset_with_base_tag.html | 12 + .../simpletest/test/site/base_tag/page_1.html | 7 + .../simpletest/test/site/base_tag/page_2.html | 7 + .../test/site/base_tag/relative_link.html | 8 + .../test/site/cookie_based_counter.php | 10 + .../test/site/counting_frameset.html | 10 + .../test/site/double_base_change_redirect.php | 6 + .../lib/simpletest/test/site/file.html | 6 + .../lib/simpletest/test/site/form.html | 47 + .../test/site/form_data_encoded_form.html | 47 + .../site/form_with_array_based_inputs.php | 15 + .../test/site/form_with_false_defaults.html | 40 + .../site/form_with_mixed_post_and_get.html | 15 + .../test/site/form_with_quoted_values.php | 14 + .../test/site/form_with_radio_buttons.html | 10 + .../test/site/form_with_tricky_defaults.html | 35 + .../test/site/form_with_unnamed_submit.html | 12 + .../test/site/form_without_action.php | 11 + .../lib/simpletest/test/site/frame_a.html | 6 + .../lib/simpletest/test/site/frame_b.html | 6 + .../lib/simpletest/test/site/frame_links.html | 7 + .../lib/simpletest/test/site/frameset.html | 10 + .../site/front_controller_style/a_page.php | 37 + .../site/front_controller_style/index.php | 44 + .../front_controller_style/show_request.php | 49 + .../lib/simpletest/test/site/link_confirm.php | 18 + .../simpletest/test/site/local_redirect.php | 6 + .../simpletest/test/site/messy_frameset.html | 16 + .../test/site/multiple_widget_form.html | 59 + .../simpletest/test/site/nested_frameset.html | 10 + .../simpletest/test/site/network_confirm.php | 84 + .../test/site/one_page_frameset.html | 9 + .../lib/simpletest/test/site/page_request.php | 60 + .../test/site/path/base_change_redirect.php | 6 + .../test/site/path/network_confirm.php | 71 + .../test/site/path/show_cookies.php | 18 + .../simpletest/test/site/protected/.htaccess | 5 + .../simpletest/test/site/protected/.htpasswd | 1 + .../lib/simpletest/test/site/protected/1.html | 6 + .../lib/simpletest/test/site/protected/2.html | 6 + .../lib/simpletest/test/site/protected/3.html | 6 + .../simpletest/test/site/protected/htaccess | 4 + .../test/site/protected/local_redirect.php | 6 + .../test/site/protected/network_confirm.php | 71 + .../lib/simpletest/test/site/redirect.php | 6 + .../test/site/savant_style_form.html | 20 + .../lib/simpletest/test/site/search.png | Bin 0 -> 1259 bytes .../lib/simpletest/test/site/self.php | 5 + .../lib/simpletest/test/site/self_form.php | 21 + .../lib/simpletest/test/site/set_cookies.php | 20 + .../lib/simpletest/test/site/slow_page.php | 6 + .../test/site/temp/.stop_cvs_removing_temp | 0 .../lib/simpletest/test/site/timestamp.php | 3 + .../lib/simpletest/test/site/upload_form.html | 11 + .../simpletest/test/site/upload_handler.php | 18 + .../lib/simpletest/test/socket_test.php | 25 + .../test/support/collector/collectable.1 | 0 .../test/support/collector/collectable.2 | 0 .../test/support/empty_test_file.php | 3 + .../lib/simpletest/test/support/latin1_sample | 1 + .../simpletest/test/support/spl_examples.php | 15 + .../support/supplementary_upload_sample.txt | 1 + .../lib/simpletest/test/support/test1.php | 7 + .../simpletest/test/support/upload_sample.txt | 1 + .../lib/simpletest/test/tag_test.php | 554 + .../lib/simpletest/test/unit_tester_test.php | 61 + .../lib/simpletest/test/unit_tests.php | 46 + .../lib/simpletest/test/url_test.php | 496 + .../lib/simpletest/test/user_agent_test.php | 348 + .../lib/simpletest/test/utf8_test.php | 78 + .../simpletest/test/visual/visual_errors.php | 65 + .../lib/simpletest/test/visual_test.php | 495 + .../lib/simpletest/test/web_tester_test.php | 155 + .../lib/simpletest/test/xml_test.php | 187 + .../test-suite/lib/simpletest/test_case.php | 655 + .../tutorials/SimpleTest/Expectations.pkg | 319 + .../tutorials/SimpleTest/FormTesting.pkg | 218 + .../tutorials/SimpleTest/GroupTests.pkg | 297 + .../tutorials/SimpleTest/MockObjects.pkg | 663 + .../tutorials/SimpleTest/PartialMock.pkg | 361 + .../tutorials/SimpleTest/Reporting.pkg | 450 + .../tutorials/SimpleTest/ServerStubs.pkg | 323 + .../tutorials/SimpleTest/SimpleTest.pkg | 387 + .../tutorials/SimpleTest/SimpleTest.pkg.ini | 10 + .../tutorials/SimpleTest/UnitTestCase.pkg | 405 + .../tutorials/SimpleTest/WebTester.pkg | 610 + .../test-suite/lib/simpletest/unit_tester.php | 402 + .../test-suite/lib/simpletest/url.php | 550 + .../test-suite/lib/simpletest/user_agent.php | 328 + .../test-suite/lib/simpletest/web_tester.php | 1495 ++ .../test-suite/lib/simpletest/xml.php | 647 + .../test-suite/lib/yaymock/classes/Yay.php | 261 + .../lib/yaymock/classes/Yay/Action.php | 37 + .../classes/Yay/Actions/CallbackAction.php | 66 + .../Yay/Actions/ReturnReferenceAction.php | 64 + .../classes/Yay/Actions/ReturnValueAction.php | 100 + .../classes/Yay/Actions/ThrowAction.php | 66 + .../lib/yaymock/classes/Yay/Description.php | 45 + .../lib/yaymock/classes/Yay/Expectation.php | 88 + .../classes/Yay/ExpectationProvider.php | 33 + .../lib/yaymock/classes/Yay/Expectations.php | 306 + .../Yay/Expectations/AbstractExpectation.php | 345 + .../Yay/Expectations/AtLeastExpectation.php | 101 + .../Yay/Expectations/AtMostExpectation.php | 117 + .../Yay/Expectations/BetweenExpectation.php | 118 + .../Yay/Expectations/ExactlyExpectation.php | 117 + .../lib/yaymock/classes/Yay/Invocation.php | 49 + .../yaymock/classes/Yay/InvocationHandler.php | 36 + .../yaymock/classes/Yay/InvocationProxy.php | 90 + .../classes/Yay/InvocationRecorder.php | 36 + .../lib/yaymock/classes/Yay/Matcher.php | 48 + .../classes/Yay/Matchers/AnyMatcher.php | 85 + .../classes/Yay/Matchers/BoundsMatcher.php | 94 + .../classes/Yay/Matchers/EqualMatcher.php | 50 + .../classes/Yay/Matchers/IdenticalMatcher.php | 116 + .../classes/Yay/Matchers/OptionalMatcher.php | 103 + .../classes/Yay/Matchers/PatternMatcher.php | 88 + .../classes/Yay/Matchers/ReferenceMatcher.php | 104 + .../lib/yaymock/classes/Yay/MockGenerator.php | 323 + .../lib/yaymock/classes/Yay/MockObject.php | 25 + .../lib/yaymock/classes/Yay/Mockery.php | 183 + .../classes/Yay/NotSatisfiedException.php | 36 + .../yaymock/classes/Yay/SelfDescribing.php | 35 + .../lib/yaymock/classes/Yay/Sequence.php | 43 + .../yaymock/classes/Yay/SimpleDescription.php | 63 + .../yaymock/classes/Yay/SimpleInvocation.php | 163 + .../yaymock/classes/Yay/SimpleSequence.php | 108 + .../lib/yaymock/classes/Yay/SimpleState.php | 49 + .../classes/Yay/SimpleStatePredicate.php | 88 + .../lib/yaymock/classes/Yay/State.php | 34 + .../lib/yaymock/classes/Yay/StateMachine.php | 113 + .../yaymock/classes/Yay/StatePredicate.php | 35 + .../lib/yaymock/classes/Yay/States.php | 62 + .../test-suite/lib/yaymock/mock.tpl.php | 69 + .../lib/yaymock/yay_convenience.php | 176 + .../test-suite/lib/yaymock/yay_mock.php | 29 + .../swiftmailer/test-suite/run.php | 56 + .../swiftmailer/test-suite/sweety.js | 471 + .../test-suite/templates/sweety/css/main.css | 203 + .../templates/sweety/images/darr.gif | Bin 0 -> 57 bytes .../templates/sweety/images/group.gif | Bin 0 -> 90 bytes .../templates/sweety/images/htmlicon.gif | Bin 0 -> 1022 bytes .../templates/sweety/images/loading.gif | Bin 0 -> 1732 bytes .../templates/sweety/images/network.gif | Bin 0 -> 2130 bytes .../templates/sweety/images/rarr.gif | Bin 0 -> 59 bytes .../templates/sweety/images/runicon.gif | Bin 0 -> 159 bytes .../templates/sweety/images/xmlicon.gif | Bin 0 -> 128 bytes .../templates/sweety/js/sweety-template.js | 1155 ++ .../templates/sweety/suite-ui-noajax.tpl.php | 155 + .../templates/sweety/suite-ui.tpl.php | 132 + .../swiftmailer/test-suite/xpath-legacy.js | 2764 +++ .../_samples/charsets/iso-2022-jp/one.txt | 11 + .../_samples/charsets/iso-8859-1/one.txt | 19 + .../tests/_samples/charsets/utf-8/one.txt | 22 + .../tests/_samples/charsets/utf-8/three.txt | 45 + .../tests/_samples/charsets/utf-8/two.txt | 3 + .../tests/_samples/dkim/dkim.test.priv | 15 + .../tests/_samples/dkim/dkim.test.pub | 6 + .../swiftmailer/tests/_samples/files/data.txt | 1 + .../tests/_samples/files/textfile.zip | Bin 0 -> 202 bytes .../swiftmailer/tests/_samples/smime/ca.crt | 33 + .../swiftmailer/tests/_samples/smime/ca.key | 54 + .../tests/_samples/smime/encrypt.crt | 33 + .../tests/_samples/smime/encrypt.key | 54 + .../tests/_samples/smime/encrypt.p12 | Bin 0 -> 4141 bytes .../tests/_samples/smime/encrypt2.crt | 33 + .../tests/_samples/smime/encrypt2.key | 54 + .../swiftmailer/tests/_samples/smime/sign.crt | 28 + .../swiftmailer/tests/_samples/smime/sign.csr | 17 + .../swiftmailer/tests/_samples/smime/sign.key | 27 + .../swiftmailer/tests/_samples/smime/sign.p12 | Bin 0 -> 2725 bytes .../tests/acceptance.conf.php.default | 44 + .../Swift/AttachmentAcceptanceTest.php | 13 + .../FileByteStreamAcceptanceTest.php | 171 + ...leCharacterReaderFactoryAcceptanceTest.php | 183 + .../DependencyContainerAcceptanceTest.php | 22 + .../Swift/EmbeddedFileAcceptanceTest.php | 13 + .../Encoder/Base64EncoderAcceptanceTest.php | 49 + .../Swift/Encoder/QpEncoderAcceptanceTest.php | 58 + .../Encoder/Rfc2231EncoderAcceptanceTest.php | 56 + .../Swift/EncodingAcceptanceTest.php | 31 + .../KeyCache/ArrayKeyCacheAcceptanceTest.php | 179 + .../KeyCache/DiskKeyCacheAcceptanceTest.php | 189 + .../Swift/MessageAcceptanceTest.php | 58 + .../Swift/Mime/AttachmentAcceptanceTest.php | 137 + .../Base64ContentEncoderAcceptanceTest.php | 62 + .../NativeQpContentEncoderAcceptanceTest.php | 95 + .../PlainContentEncoderAcceptanceTest.php | 96 + .../QpContentEncoderAcceptanceTest.php | 170 + .../Swift/Mime/EmbeddedFileAcceptanceTest.php | 150 + .../Base64HeaderEncoderAcceptanceTest.php | 35 + .../Swift/Mime/MimePartAcceptanceTest.php | 143 + .../Mime/SimpleMessageAcceptanceTest.php | 1254 ++ .../Swift/MimePartAcceptanceTest.php | 15 + .../AbstractStreamBufferAcceptanceTest.php | 122 + .../BasicSocketAcceptanceTest.php | 31 + .../StreamBuffer/ProcessAcceptanceTest.php | 23 + .../StreamBuffer/SocketTimeoutTest.php | 72 + .../StreamBuffer/SslSocketAcceptanceTest.php | 35 + .../StreamBuffer/TlsSocketAcceptanceTest.php | 35 + .../tests/bug/Swift/Bug111Test.php | 44 + .../tests/bug/Swift/Bug118Test.php | 22 + .../tests/bug/Swift/Bug206Test.php | 40 + .../tests/bug/Swift/Bug274Test.php | 23 + .../swiftmailer/tests/bug/Swift/Bug34Test.php | 77 + .../swiftmailer/tests/bug/Swift/Bug35Test.php | 75 + .../swiftmailer/tests/bug/Swift/Bug38Test.php | 196 + .../swiftmailer/tests/bug/Swift/Bug51Test.php | 125 + .../swiftmailer/tests/bug/Swift/Bug71Test.php | 22 + .../swiftmailer/tests/bug/Swift/Bug76Test.php | 86 + .../Tests/IdenticalBinaryExpectation.php | 79 + .../Swift/Tests/SwiftSmokeTestCase.php | 53 + .../helpers/Swift/Tests/SwiftUnitTestCase.php | 100 + .../swiftmailer/tests/smoke.conf.php.default | 63 + .../smoke/Swift/Smoke/AttachmentSmokeTest.php | 30 + .../smoke/Swift/Smoke/BasicSmokeTest.php | 24 + .../Smoke/HtmlWithAttachmentSmokeTest.php | 30 + .../Swift/Smoke/InternationalSmokeTest.php | 37 + .../Swift/ByteStream/ArrayByteStreamTest.php | 204 + .../GenericFixedWidthReaderTest.php | 46 + .../CharacterReader/UsAsciiReaderTest.php | 55 + .../Swift/CharacterReader/Utf8ReaderTest.php | 68 + .../ArrayCharacterStreamTest.php | 375 + .../unit/Swift/DependencyContainerTest.php | 177 + .../unit/Swift/Encoder/Base64EncoderTest.php | 175 + .../unit/Swift/Encoder/QpEncoderTest.php | 361 + .../unit/Swift/Encoder/Rfc2231EncoderTest.php | 143 + .../unit/Swift/Events/CommandEventTest.php | 41 + .../unit/Swift/Events/EventObjectTest.php | 37 + .../unit/Swift/Events/ResponseEventTest.php | 44 + .../tests/unit/Swift/Events/SendEventTest.php | 104 + .../Events/SimpleEventDispatcherTest.php | 167 + .../Swift/Events/TransportChangeEventTest.php | 36 + .../Events/TransportExceptionEventTest.php | 49 + .../unit/Swift/KeyCache/ArrayKeyCacheTest.php | 242 + .../SimpleKeyCacheInputStreamTest.php | 77 + .../Mailer/ArrayRecipientIteratorTest.php | 46 + .../tests/unit/Swift/MailerTest.php | 149 + .../Swift/Mime/AbstractMimeEntityTest.php | 1102 + .../tests/unit/Swift/Mime/AttachmentTest.php | 298 + .../Base64ContentEncoderTest.php | 295 + .../PlainContentEncoderTest.php | 277 + .../ContentEncoder/QpContentEncoderTest.php | 476 + .../unit/Swift/Mime/EmbeddedFileTest.php | 63 + .../HeaderEncoder/Base64HeaderEncoderTest.php | 15 + .../HeaderEncoder/QpHeaderEncoderTest.php | 222 + .../Swift/Mime/Headers/DateHeaderTest.php | 76 + .../Mime/Headers/IdentificationHeaderTest.php | 199 + .../Swift/Mime/Headers/MailboxHeaderTest.php | 340 + .../Mime/Headers/ParameterizedHeaderTest.php | 422 + .../Swift/Mime/Headers/PathHeaderTest.php | 84 + .../Mime/Headers/UnstructuredHeaderTest.php | 366 + .../tests/unit/Swift/Mime/MimePartTest.php | 252 + .../Swift/Mime/SimpleHeaderFactoryTest.php | 179 + .../unit/Swift/Mime/SimpleHeaderSetTest.php | 653 + .../unit/Swift/Mime/SimpleMessageTest.php | 801 + .../unit/Swift/Mime/SimpleMimeEntityTest.php | 15 + .../Swift/Plugins/AntiFloodPluginTest.php | 99 + .../Plugins/BandwidthMonitorPluginTest.php | 129 + .../Swift/Plugins/DecoratorPluginTest.php | 235 + .../unit/Swift/Plugins/LoggerPluginTest.php | 198 + .../Swift/Plugins/Loggers/ArrayLoggerTest.php | 70 + .../Swift/Plugins/Loggers/EchoLoggerTest.php | 29 + .../Swift/Plugins/PopBeforeSmtpPluginTest.php | 114 + .../Swift/Plugins/RedirectingPluginTest.php | 113 + .../unit/Swift/Plugins/ReporterPluginTest.php | 120 + .../Plugins/Reporters/HitReporterTest.php | 69 + .../Plugins/Reporters/HtmlReporterTest.php | 59 + .../Swift/Plugins/ThrottlerPluginTest.php | 126 + .../unit/Swift/Signers/DKIMSignerTest.php | 212 + .../unit/Swift/Signers/SMimeSignerTest.php | 515 + .../ByteArrayReplacementFilterTest.php | 135 + .../StringReplacementFilterFactoryTest.php | 41 + .../StringReplacementFilterTest.php | 59 + .../AbstractSmtpEventSupportTest.php | 378 + .../unit/Swift/Transport/AbstractSmtpTest.php | 924 + .../Esmtp/Auth/CramMd5AuthenticatorTest.php | 67 + .../Esmtp/Auth/LoginAuthenticatorTest.php | 60 + .../Esmtp/Auth/PlainAuthenticatorTest.php | 71 + .../Swift/Transport/Esmtp/AuthHandlerTest.php | 157 + .../EsmtpTransport/ExtensionSupportTest.php | 314 + .../Swift/Transport/EsmtpTransportTest.php | 235 + .../Swift/Transport/FailoverTransportTest.php | 335 + .../Transport/LoadBalancedTransportTest.php | 419 + .../Swift/Transport/MailTransportTest.php | 315 + .../Swift/Transport/SendmailTransportTest.php | 135 + .../unit/Swift/Transport/StreamBufferTest.php | 50 + 1511 files changed, 297672 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 app/commands/.gitkeep create mode 100644 app/config/app.php create mode 100644 app/config/auth.php create mode 100644 app/config/cache.php create mode 100644 app/config/compile.php create mode 100644 app/config/database.php create mode 100644 app/config/local/.gitignore create mode 100644 app/config/mail.php create mode 100644 app/config/packages/.gitkeep create mode 100644 app/config/queue.php create mode 100644 app/config/session.php create mode 100644 app/config/testing/cache.php create mode 100644 app/config/testing/session.php create mode 100644 app/config/view.php create mode 100644 app/config/workbench.php create mode 100644 app/controllers/AccountController.php create mode 100644 app/controllers/AlbumsController.php create mode 100644 app/controllers/Api/Web/AuthController.php create mode 100644 app/controllers/Api/Web/TaxonomiesController.php create mode 100644 app/controllers/Api/Web/TracksController.php create mode 100644 app/controllers/ApiControllerBase.php create mode 100644 app/controllers/ArtistsController.php create mode 100644 app/controllers/ContentController.php create mode 100644 app/controllers/FavoritesController.php create mode 100644 app/controllers/HomeController.php create mode 100644 app/controllers/PlaylistsController.php create mode 100644 app/controllers/TracksController.php create mode 100644 app/database/migrations/.gitkeep create mode 100644 app/database/migrations/2013_06_07_003952_create_users_table.php create mode 100644 app/database/migrations/2013_06_27_015259_create_tracks_table.php create mode 100644 app/database/production.sqlite create mode 100644 app/database/seeds/.gitkeep create mode 100644 app/database/seeds/DatabaseSeeder.php create mode 100644 app/filters.php create mode 100644 app/lang/en/pagination.php create mode 100644 app/lang/en/reminders.php create mode 100644 app/lang/en/validation.php create mode 100644 app/library/Assets.php create mode 100644 app/library/AudioCache.php create mode 100644 app/library/CacheBusterAsset.php create mode 100644 app/library/Helpers.php create mode 100644 app/library/IpsHasher.php create mode 100644 app/library/PfmAuth.php create mode 100644 app/library/PfmValidator.php create mode 100644 app/models/Commands/CommandBase.php create mode 100644 app/models/Commands/CommandResponse.php create mode 100644 app/models/Commands/DeleteTrackCommand.php create mode 100644 app/models/Commands/EditTrackCommand.php create mode 100644 app/models/Commands/UploadTrackCommand.php create mode 100644 app/models/Entities/Genre.php create mode 100644 app/models/Entities/License.php create mode 100644 app/models/Entities/Track.php create mode 100644 app/models/Entities/TrackType.php create mode 100644 app/models/Entities/User.php create mode 100644 app/routes.php create mode 100644 app/start/artisan.php create mode 100644 app/start/global.php create mode 100644 app/start/local.php create mode 100644 app/storage/.gitignore create mode 100644 app/storage/cache/.gitignore create mode 100644 app/storage/logs/.gitignore create mode 100644 app/storage/meta/.gitignore create mode 100644 app/storage/scripts/.gitignore create mode 100644 app/storage/sessions/.gitignore create mode 100644 app/storage/styles/.gitignore create mode 100644 app/storage/views/.gitignore create mode 100644 app/tests/ExampleTest.php create mode 100644 app/tests/TestCase.php create mode 100644 app/views/albums/index.blade.php create mode 100644 app/views/artists/index.blade.php create mode 100644 app/views/auth/login.blade.php create mode 100644 app/views/auth/register.blade.php create mode 100644 app/views/emails/auth/reminder.blade.php create mode 100644 app/views/home/index.blade.php create mode 100644 app/views/pages/about.blade.php create mode 100644 app/views/pages/faq.blade.php create mode 100644 app/views/playlists/index.blade.php create mode 100644 app/views/shared/_app_layout.blade.php create mode 100644 app/views/shared/_layout.blade.php create mode 100644 app/views/shared/null.blade.php create mode 100644 app/views/tracks/index.blade.php create mode 100644 artisan create mode 100644 bootstrap/autoload.php create mode 100644 bootstrap/compiled.php create mode 100644 bootstrap/paths.php create mode 100644 bootstrap/start.php create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 composer.phar create mode 100644 phpunit.xml create mode 100644 public/.htaccess create mode 100644 public/asset.php create mode 100644 public/favicon.ico create mode 100644 public/fonts/FontAwesome.otf create mode 100644 public/fonts/fontawesome-webfont.eot create mode 100644 public/fonts/fontawesome-webfont.svg create mode 100644 public/fonts/fontawesome-webfont.ttf create mode 100644 public/fonts/fontawesome-webfont.woff create mode 100644 public/images/glyphicons-halflings-white.png create mode 100644 public/images/glyphicons-halflings.png create mode 100644 public/index.php create mode 100644 public/robots.txt create mode 100644 public/scripts/app/app.coffee create mode 100644 public/scripts/app/controllers/account-content-tracks.coffee create mode 100644 public/scripts/app/controllers/account-settings.coffee create mode 100644 public/scripts/app/controllers/application.coffee create mode 100644 public/scripts/app/controllers/login.coffee create mode 100644 public/scripts/app/controllers/upload.coffee create mode 100644 public/scripts/app/directives/eat-click.coffee create mode 100644 public/scripts/app/directives/progress-bar.coffee create mode 100644 public/scripts/app/directives/uploader.coffee create mode 100644 public/scripts/app/filters/length.coffee create mode 100644 public/scripts/app/filters/pfm-date.js create mode 100644 public/scripts/app/services/auth.coffee create mode 100644 public/scripts/app/services/taxonomies.coffee create mode 100644 public/scripts/app/services/upload.coffee create mode 100644 public/scripts/base/angular-ui-date.js create mode 100644 public/scripts/base/angular-ui-router.js create mode 100644 public/scripts/base/angular.js create mode 100644 public/scripts/base/jquery-2.0.2.js create mode 100644 public/scripts/base/jquery-ui.js create mode 100644 public/scripts/base/ui-bootstrap-tpls-0.4.0.js create mode 100644 public/scripts/base/underscore.js create mode 100644 public/scripts/shared/layout.coffee create mode 100644 public/styles/account-tracks.less create mode 100644 public/styles/app.less create mode 100644 public/styles/base/bootstrap/accordion.less create mode 100644 public/styles/base/bootstrap/alerts.less create mode 100644 public/styles/base/bootstrap/bootstrap.less create mode 100644 public/styles/base/bootstrap/breadcrumbs.less create mode 100644 public/styles/base/bootstrap/button-groups.less create mode 100644 public/styles/base/bootstrap/buttons.less create mode 100644 public/styles/base/bootstrap/carousel.less create mode 100644 public/styles/base/bootstrap/close.less create mode 100644 public/styles/base/bootstrap/code.less create mode 100644 public/styles/base/bootstrap/component-animations.less create mode 100644 public/styles/base/bootstrap/dropdowns.less create mode 100644 public/styles/base/bootstrap/forms.less create mode 100644 public/styles/base/bootstrap/grid.less create mode 100644 public/styles/base/bootstrap/hero-unit.less create mode 100644 public/styles/base/bootstrap/labels-badges.less create mode 100644 public/styles/base/bootstrap/layouts.less create mode 100644 public/styles/base/bootstrap/media.less create mode 100644 public/styles/base/bootstrap/mixins.less create mode 100644 public/styles/base/bootstrap/modals.less create mode 100644 public/styles/base/bootstrap/navbar.less create mode 100644 public/styles/base/bootstrap/navs.less create mode 100644 public/styles/base/bootstrap/pager.less create mode 100644 public/styles/base/bootstrap/pagination.less create mode 100644 public/styles/base/bootstrap/popovers.less create mode 100644 public/styles/base/bootstrap/progress-bars.less create mode 100644 public/styles/base/bootstrap/reset.less create mode 100644 public/styles/base/bootstrap/responsive-1200px-min.less create mode 100644 public/styles/base/bootstrap/responsive-767px-max.less create mode 100644 public/styles/base/bootstrap/responsive-768px-979px.less create mode 100644 public/styles/base/bootstrap/responsive-navbar.less create mode 100644 public/styles/base/bootstrap/responsive-utilities.less create mode 100644 public/styles/base/bootstrap/responsive.less create mode 100644 public/styles/base/bootstrap/scaffolding.less create mode 100644 public/styles/base/bootstrap/sprites.less create mode 100644 public/styles/base/bootstrap/tables.less create mode 100644 public/styles/base/bootstrap/thumbnails.less create mode 100644 public/styles/base/bootstrap/tooltip.less create mode 100644 public/styles/base/bootstrap/type.less create mode 100644 public/styles/base/bootstrap/utilities.less create mode 100644 public/styles/base/bootstrap/variables.less create mode 100644 public/styles/base/bootstrap/wells.less create mode 100644 public/styles/base/font-awesome/bootstrap.less create mode 100644 public/styles/base/font-awesome/core.less create mode 100644 public/styles/base/font-awesome/extras.less create mode 100644 public/styles/base/font-awesome/font-awesome-ie7.less create mode 100644 public/styles/base/font-awesome/font-awesome.less create mode 100644 public/styles/base/font-awesome/icons.less create mode 100644 public/styles/base/font-awesome/mixins.less create mode 100644 public/styles/base/font-awesome/path.less create mode 100644 public/styles/base/font-awesome/variables.less create mode 100644 public/styles/base/images/animated-overlay.gif create mode 100644 public/styles/base/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 public/styles/base/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 public/styles/base/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 public/styles/base/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 public/styles/base/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 public/styles/base/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 public/styles/base/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 public/styles/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 public/styles/base/images/ui-icons_222222_256x240.png create mode 100644 public/styles/base/images/ui-icons_2e83ff_256x240.png create mode 100644 public/styles/base/images/ui-icons_454545_256x240.png create mode 100644 public/styles/base/images/ui-icons_888888_256x240.png create mode 100644 public/styles/base/images/ui-icons_cd0a0a_256x240.png create mode 100644 public/styles/base/jquery-ui.css create mode 100644 public/styles/components.less create mode 100644 public/styles/home.less create mode 100644 public/styles/layout.less create mode 100644 public/styles/mixins.less create mode 100644 public/templates/account/content/_layout.html create mode 100644 public/templates/account/content/albums.html create mode 100644 public/templates/account/content/playlists.html create mode 100644 public/templates/account/content/tracks.html create mode 100644 public/templates/account/favorites/_layout.html create mode 100644 public/templates/account/favorites/albums.html create mode 100644 public/templates/account/favorites/playlists.html create mode 100644 public/templates/account/favorites/tracks.html create mode 100644 public/templates/account/settings.html create mode 100644 public/templates/albums/index.html create mode 100644 public/templates/artists/index.html create mode 100644 public/templates/auth/login.html create mode 100644 public/templates/auth/register.html create mode 100644 public/templates/home/index.html create mode 100644 public/templates/pages/about.html create mode 100644 public/templates/pages/faq.html create mode 100644 public/templates/partials/auth/login.html create mode 100644 public/templates/partials/upload-dialog.html create mode 100644 public/templates/playlists/index.html create mode 100644 public/templates/tracks/_layout.html create mode 100644 public/templates/tracks/index.html create mode 100644 readme.md create mode 100644 server.php create mode 100644 spa.pony.fm.iml create mode 100644 vendor/autoload.php create mode 100644 vendor/bin/classpreloader.php create mode 100644 vendor/bin/classpreloader.php.bat create mode 100644 vendor/codescale/ffmpeg-php/.gitignore create mode 100644 vendor/codescale/ffmpeg-php/FFmpegAnimatedGif.php create mode 100644 vendor/codescale/ffmpeg-php/FFmpegAutoloader.php create mode 100644 vendor/codescale/ffmpeg-php/FFmpegFrame.php create mode 100644 vendor/codescale/ffmpeg-php/FFmpegMovie.php create mode 100644 vendor/codescale/ffmpeg-php/LICENSE create mode 100644 vendor/codescale/ffmpeg-php/README.rst create mode 100644 vendor/codescale/ffmpeg-php/adapter/ffmpeg_animated_gif.php create mode 100644 vendor/codescale/ffmpeg-php/adapter/ffmpeg_frame.php create mode 100644 vendor/codescale/ffmpeg-php/adapter/ffmpeg_movie.php create mode 100644 vendor/codescale/ffmpeg-php/composer.json create mode 100644 vendor/codescale/ffmpeg-php/package.xml create mode 100644 vendor/codescale/ffmpeg-php/provider/AbstractOutputProvider.php create mode 100644 vendor/codescale/ffmpeg-php/provider/FFmpegOutputProvider.php create mode 100644 vendor/codescale/ffmpeg-php/provider/FFprobeOutputProvider.php create mode 100644 vendor/codescale/ffmpeg-php/provider/OutputProvider.php create mode 100644 vendor/codescale/ffmpeg-php/provider/StringOutputProvider.php create mode 100644 vendor/codescale/ffmpeg-php/test/FFmpegAnimatedGifTest.php create mode 100644 vendor/codescale/ffmpeg-php/test/FFmpegAutoloaderTest.php create mode 100644 vendor/codescale/ffmpeg-php/test/FFmpegFrameTest.php create mode 100644 vendor/codescale/ffmpeg-php/test/FFmpegMovieTest.php create mode 100644 vendor/codescale/ffmpeg-php/test/adapter/ffmpeg_animated_gif_Test.php create mode 100644 vendor/codescale/ffmpeg-php/test/adapter/ffmpeg_frame_Test.php create mode 100644 vendor/codescale/ffmpeg-php/test/adapter/ffmpeg_movie_Test.php create mode 100644 vendor/codescale/ffmpeg-php/test/bootstrap.php create mode 100644 vendor/codescale/ffmpeg-php/test/data/test.mp4 create mode 100644 vendor/codescale/ffmpeg-php/test/data/test.wav create mode 100644 vendor/codescale/ffmpeg-php/test/data/test1.txt create mode 100644 vendor/codescale/ffmpeg-php/test/provider/FFmpegOutputProviderTest.php create mode 100644 vendor/codescale/ffmpeg-php/test/provider/FFprobeOutputProviderTest.php create mode 100644 vendor/composer/ClassLoader.php create mode 100644 vendor/composer/autoload_classmap.php create mode 100644 vendor/composer/autoload_namespaces.php create mode 100644 vendor/composer/autoload_real.php create mode 100644 vendor/composer/installed.json create mode 100644 vendor/doctrine/annotations/.gitignore create mode 100644 vendor/doctrine/annotations/.travis.yml create mode 100644 vendor/doctrine/annotations/README.md create mode 100644 vendor/doctrine/annotations/composer.json create mode 100644 vendor/doctrine/annotations/composer.lock create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php create mode 100644 vendor/doctrine/annotations/phpunit.xml.dist create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AbstractReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DummyClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/FileCacheReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/AnnotWithDefaultValue.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Autoload.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Route.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Secure.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Template.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Version.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnum.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumInvalid.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteral.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteralInvalid.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetAll.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetAnnotation.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetMethod.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetPropertyMethod.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithAttributes.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithConstants.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributes.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributesWithoutContructor.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithTargetSyntaxError.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithVarType.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Api.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassDDC1660.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationEnum.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationWithTargetSyntaxError.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationWithVarType.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithClosure.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithConstants.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithFullyQualifiedUseStatements.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtMethod.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtProperty.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithRequire.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithValidAnnotationTarget.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Controller.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsFirst.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsLast.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/EqualNamespacesPerFileWithClassAsFirst.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/EqualNamespacesPerFileWithClassAsLast.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsFirst.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsLast.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/IntefaceWithConstants.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/InvalidAnnotationUsageButIgnoredClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/InvalidAnnotationUsageClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/MultipleClassesInFile.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/MultipleImportsInUseStatement.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespaceAndClassCommentedOut.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespaceWithClosureDeclaration.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespacedSingleClassLOC1000.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NoAnnotation.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NonNamespacedClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/TestInterface.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PerformanceTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM55Test.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Test.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/TopLevelAnnotation.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/DoctrineTestCase.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/TestInit.php create mode 100644 vendor/filp/whoops/.gitignore create mode 100644 vendor/filp/whoops/.travis.yml create mode 100644 vendor/filp/whoops/LICENSE.md create mode 100644 vendor/filp/whoops/README.md create mode 100644 vendor/filp/whoops/composer.json create mode 100644 vendor/filp/whoops/composer.lock create mode 100644 vendor/filp/whoops/examples/example-ajax-only.php create mode 100644 vendor/filp/whoops/examples/example-silex.php create mode 100644 vendor/filp/whoops/examples/example.php create mode 100644 vendor/filp/whoops/phpunit.xml.dist create mode 100644 vendor/filp/whoops/src/Whoops/Exception/ErrorException.php create mode 100644 vendor/filp/whoops/src/Whoops/Exception/Frame.php create mode 100644 vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php create mode 100644 vendor/filp/whoops/src/Whoops/Exception/Inspector.php create mode 100644 vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php create mode 100644 vendor/filp/whoops/src/Whoops/Handler/Handler.php create mode 100644 vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php create mode 100644 vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php create mode 100644 vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php create mode 100644 vendor/filp/whoops/src/Whoops/Provider/Silex/WhoopsServiceProvider.php create mode 100644 vendor/filp/whoops/src/Whoops/Provider/Zend/ExceptionStrategy.php create mode 100644 vendor/filp/whoops/src/Whoops/Provider/Zend/Module.php create mode 100644 vendor/filp/whoops/src/Whoops/Provider/Zend/RouteNotFoundStrategy.php create mode 100644 vendor/filp/whoops/src/Whoops/Provider/Zend/module.config.example.php create mode 100644 vendor/filp/whoops/src/Whoops/Resources/pretty-page.css create mode 100644 vendor/filp/whoops/src/Whoops/Resources/pretty-template.php create mode 100644 vendor/filp/whoops/src/Whoops/Run.php create mode 100644 vendor/filp/whoops/tests/Whoops/Exception/FrameCollectionTest.php create mode 100644 vendor/filp/whoops/tests/Whoops/Exception/FrameTest.php create mode 100644 vendor/filp/whoops/tests/Whoops/Exception/InspectorTest.php create mode 100644 vendor/filp/whoops/tests/Whoops/Handler/JsonResponseHandlerTest.php create mode 100644 vendor/filp/whoops/tests/Whoops/Handler/PrettyPageHandlerTest.php create mode 100644 vendor/filp/whoops/tests/Whoops/RunTest.php create mode 100644 vendor/filp/whoops/tests/Whoops/TestCase.php create mode 100644 vendor/filp/whoops/tests/bootstrap.php create mode 100644 vendor/filp/whoops/tests/fixtures/frame.lines-test.php create mode 100644 vendor/kriswallsmith/assetic/CHANGELOG-1.0.md create mode 100644 vendor/kriswallsmith/assetic/CHANGELOG-1.1.md create mode 100644 vendor/kriswallsmith/assetic/CHANGELOG-1.2.md create mode 100644 vendor/kriswallsmith/assetic/Gemfile create mode 100644 vendor/kriswallsmith/assetic/LICENSE create mode 100644 vendor/kriswallsmith/assetic/README.md create mode 100644 vendor/kriswallsmith/assetic/composer.json create mode 100644 vendor/kriswallsmith/assetic/package.json create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Cache/ArrayCache.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/ValueContainer.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/CacheBustingWorker.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseNodeFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseProcessFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/DartFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/DependencyExtractorInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/EmberPrecompileFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/HandlebarsFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/PhpCssEmbedFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/RooleFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/ScssphpFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/TypeScriptFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyCssFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJs2Filter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Util/CssUtils.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Util/LessUtils.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/Util/VarUtils.php create mode 100644 vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php create mode 100644 vendor/kriswallsmith/assetic/src/functions.php create mode 100644 vendor/monolog/monolog/CHANGELOG.mdown create mode 100644 vendor/monolog/monolog/LICENSE create mode 100644 vendor/monolog/monolog/README.mdown create mode 100644 vendor/monolog/monolog/composer.json create mode 100644 vendor/monolog/monolog/doc/extending.md create mode 100644 vendor/monolog/monolog/doc/sockets.md create mode 100644 vendor/monolog/monolog/doc/usage.md create mode 100644 vendor/monolog/monolog/phpunit.xml.dist create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Logger.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/ChromePHPFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/GelfMessageFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/LineFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/LogstashFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/WildfireFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Functional/Handler/FirePHPHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/AbstractHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/AbstractProcessingHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/AmqpExchangeMock.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/AmqpHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/BufferHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/CouchDBHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/DoctrineCouchDBHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/Fixtures/.gitkeep create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/GelfMocks.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/GroupHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/MailHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/MockRavenClient.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/MongoDBHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/NativeMailerHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/NullHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/PushoverHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/RavenHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/RedisHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/StreamHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SyslogHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/ZendMonitorHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/LoggerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/IntrospectionProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/MemoryPeakUsageProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/MemoryUsageProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/ProcessIdProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/UidProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/WebProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/PsrLogCompatTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/TestCase.php create mode 100644 vendor/monolog/monolog/tests/bootstrap.php create mode 100644 vendor/nesbot/carbon/.gitignore create mode 100644 vendor/nesbot/carbon/.travis.yml create mode 100644 vendor/nesbot/carbon/Carbon/Carbon.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/AddTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/ComparisonTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/ConstructTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/CopyTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/CreateFromDateTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/CreateFromFormatTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/CreateFromTimeTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/CreateFromTimestampTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/CreateTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/DiffTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/FluidSettersTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/GettersTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/InstanceTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/IsTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/IssetTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/NowAndOtherStaticHelpersTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/SettersTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/StartEndOfTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/StringsTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/SubTest.php create mode 100644 vendor/nesbot/carbon/Carbon/Tests/TestFixture.php create mode 100644 vendor/nesbot/carbon/LICENSE create mode 100644 vendor/nesbot/carbon/composer.json create mode 100644 vendor/nesbot/carbon/composer.lock create mode 100644 vendor/nesbot/carbon/history.md create mode 100644 vendor/nesbot/carbon/phpunit.xml.dist create mode 100644 vendor/nesbot/carbon/readme.md create mode 100644 vendor/nesbot/carbon/readme.php create mode 100644 vendor/nesbot/carbon/readme.src.md create mode 100644 vendor/patchwork/utf8/.gitattributes create mode 100644 vendor/patchwork/utf8/README.md create mode 100644 vendor/patchwork/utf8/class/Normalizer.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/Iconv.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/Intl.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/Mbstring.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/Normalizer.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/Xml.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.big5.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp037.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp1006.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp1026.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp424.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp437.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp500.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp737.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp775.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp850.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp852.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp855.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp856.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp857.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp860.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp861.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp862.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp863.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp864.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp865.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp866.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp869.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp874.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp875.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp932.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp936.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp949.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.cp950.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.gsm0338.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-1.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-10.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-11.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-13.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-14.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-15.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-16.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-2.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-3.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-4.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-5.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-6.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-7.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-8.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.iso-8859-9.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.koi8-r.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.koi8-u.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.mazovia.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.nextstep.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.stdenc.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.symbol.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.turkish.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.us-ascii-quotes.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.us-ascii.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1250.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1251.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1252.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1253.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1254.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1255.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1256.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1257.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.windows-1258.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.x-mac-ce.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.x-mac-cyrillic.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.x-mac-greek.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.x-mac-icelandic.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.x-mac-roman.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/from.zdingbat.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/to.gsm0338.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/to.mazovia.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/to.stdenc.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/to.symbol.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/to.zdingbat.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/charset/translit.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/unidata/canonicalComposition.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/unidata/canonicalDecomposition.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/unidata/combiningClass.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/unidata/compatibilityDecomposition.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/unidata/lowerCase.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/PHP/Shim/unidata/upperCase.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8/Bootup.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8/Bootup/iconv.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8/Bootup/intl.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8/Bootup/mbstring.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8/Bootup/utf8_encode.php create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8/data/caseFolding_full.ser create mode 100644 vendor/patchwork/utf8/class/Patchwork/Utf8/data/translit_extra.ser create mode 100644 vendor/patchwork/utf8/composer.json create mode 100644 vendor/psr/log/.gitignore create mode 100644 vendor/psr/log/LICENSE create mode 100644 vendor/psr/log/Psr/Log/AbstractLogger.php create mode 100644 vendor/psr/log/Psr/Log/InvalidArgumentException.php create mode 100644 vendor/psr/log/Psr/Log/LogLevel.php create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareInterface.php create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareTrait.php create mode 100644 vendor/psr/log/Psr/Log/LoggerInterface.php create mode 100644 vendor/psr/log/Psr/Log/LoggerTrait.php create mode 100644 vendor/psr/log/Psr/Log/NullLogger.php create mode 100644 vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php create mode 100644 vendor/psr/log/README.md create mode 100644 vendor/psr/log/composer.json create mode 100644 vendor/swiftmailer/swiftmailer/.gitignore create mode 100644 vendor/swiftmailer/swiftmailer/CHANGES create mode 100644 vendor/swiftmailer/swiftmailer/LICENSE create mode 100644 vendor/swiftmailer/swiftmailer/README create mode 100644 vendor/swiftmailer/swiftmailer/README.git create mode 100644 vendor/swiftmailer/swiftmailer/VERSION create mode 100644 vendor/swiftmailer/swiftmailer/build.xml create mode 100644 vendor/swiftmailer/swiftmailer/composer.json create mode 100644 vendor/swiftmailer/swiftmailer/create_pear_package.php create mode 100644 vendor/swiftmailer/swiftmailer/doc/headers.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/help-resources.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/including-the-files.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/index.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/installing.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/introduction.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/japanese.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/messages.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/overview.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/plugins.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/sending.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Base64Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/Event.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FailoverTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Image.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/NullKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Grammar.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderSet.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Exception.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/RfcComplianceException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SendmailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SpoolTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Validate.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/mime_types.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/preferences.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_init.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_required.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_required_pear.php create mode 100644 vendor/swiftmailer/swiftmailer/notes/APPS create mode 100644 vendor/swiftmailer/swiftmailer/notes/CHARSETS create mode 100644 vendor/swiftmailer/swiftmailer/notes/message.xml create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc0821.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc0822.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc1341.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc1521.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc1854.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2015.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2045.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2046.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2047.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2048.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2049.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2183.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2222.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2231.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2234.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2440.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2487.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2554.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2821.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc2822.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc3156.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc3676.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc4505.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc4616.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc4870.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc4871.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc4880.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc4954.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/rfc5751.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/rfc/whats_where.txt create mode 100644 vendor/swiftmailer/swiftmailer/notes/smtp.txt create mode 100644 vendor/swiftmailer/swiftmailer/package.xml.tpl create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/CHANGES create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/LICENSE create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/README create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/config.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/index.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Reporter/CliReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Reporter/CliTestCaseReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Reporter/HtmlReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Reporter/HtmlTestCaseReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Runner.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Runner/AbstractTestRunner.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Runner/CliRunner.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/Runner/HtmlRunner.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/TestLocator.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/Sweety/TestLocator/PearStyleLocator.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/LICENSE create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/README create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/TODO.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/VERSION create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/authentication.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/autorun.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/browser.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/collector.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/compatibility.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/cookies.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/default_reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/detached.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/en/docs.css create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/fr/docs.css create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/lastcraft/README create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/onpk/README create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/pkg/README create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/README create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/favicon.ico create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/book-domain-driven-design.jpg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/book-guide-to-php-design-patterns.jpg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/book-the-php-anthology-object-oriented-php-solutions.jpg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/quote.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-contribute.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-download.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-external-bottom.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-external-middle.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-external-top.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-internal-bottom.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-internal-middle.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-internal-top.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-logo.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-start-testing.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/simpletest-support.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/test-in-cli.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/test-with-1-fail.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/images/test-with-1-pass.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/index.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/js/jquery-1.2.1.pack.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/js/jquery-speakers_coaches_consultants.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/js/jquery.heartbeat.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/js/jquery.sparkline.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/simpletest.css create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/views/heartbeat.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/simpletest.org/views/photos_stream.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/about.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/authentication_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/books_website.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/boundary_classes_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/browser_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/changelog.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/coding_standards.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/display_subclass_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/download_website.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/expectation_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/experimental_dom_tester.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/experimental_intro.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/experimental_recorder.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/extension_eclipse.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/first_test_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/form_testing_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/gain_control_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/group_test_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/group_test_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/heartbeat.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/ideas.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/improving_design_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/index.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/intro.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/mock_objects_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/mock_objects_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/overview.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/partial_mocks_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/photos_stream.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/reporter_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/screencasts.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/simple_test.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/softwares_using_simpletest.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/speakers_coaches_consultancy.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/subclass_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/support_website.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/unit_test_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/web_tester_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/en/writing_extensions.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/authentication_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/books_website.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/boundary_classes_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/browser_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/display_subclass_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/download_website.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/expectation_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/extension_eclipse.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/first_test_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/form_testing_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/gain_control_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/group_test_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/group_test_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/improving_design_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/index.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/intro.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/logiciels_utilisant_simpletest.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/mock_objects_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/mock_objects_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/overview.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/partial_mocks_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/reporter_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/simple_test.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/subclass_tutorial.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/support_website.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/unit_test_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/docs/source/fr/web_tester_documentation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/dumper.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/eclipse.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/encoding.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/errors.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/exceptions.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/expectation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/colortext_reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/css/webunit.css create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/dom_tester.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/dom_tester/css_selector.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/dom_tester/test/dom_tester_doc_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/dom_tester/test/dom_tester_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/dom_tester/test/support/child_adjacent.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/dom_tester/test/support/dom_tester.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/img/wait.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/js/tests/TestOfWebunit.js.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/js/webunit.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/js/x.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/junit_xml_reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/pear_test_case.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/recorder.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/recorder/test/sample.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/recorder/test/test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/selenese_tester.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/selenium.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/selenium/remote-control.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/selenium/test/remote-control_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/testdox.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/testdox/test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/treemap_reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/treemap_reporter/jquery.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/treemap_reporter/test/treemap_node_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/treemap_reporter/treemap_recorder.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/extensions/webunit_reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/form.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/frames.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/http.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/invoker.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/mock_objects.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/README create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/build_tarball.sh create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/bundled_docs.xslt create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/bundled_map.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/extension.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/generate_package.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/lastcraft.xslt create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/make_bundled_docs.sh create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/make_bundled_docs_with_xalan.sh create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/make_lastcraft_docs.sh create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/make_phpdoc_docs.sh create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/onpk/map_onpk.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/onpk/onpk.xslt create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/onpk/transform_all_onpk.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/package.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/pear_package_create.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/phpdoc_docs.xslt create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.ini create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/index.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/integration.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/map.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/package.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/template.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/content_without_section.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/en/synchronisation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/fr/no-synchronisation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/fr/synchronisation.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/here_download.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/here_overview.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/here_simpletest.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/here_start_testing.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/here_support.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/here_unit-tester.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/map.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/one_section_changelogged.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/one_section_milestoned.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/one_section_with_autorum_php.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package/one_section_with_php_code.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/simpletest.org/test/package_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/site_map.xml create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/packages/transform_all_lastcraft.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/page.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/parser.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/reflection_php4.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/reflection_php5.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/remote.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/scorer.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/selector.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/shell_tester.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/simpletest.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/socket.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tag.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/acceptance_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/adapter_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/all_tests.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/authentication_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/autorun_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/bad_test_suite.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/browser_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/collector_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/command_line_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/compatibility_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/cookies_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/detached_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/dumper_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/eclipse_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/encoding_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/errors_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/exceptions_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/expectation_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/extensions_tests.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/form_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/frames_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/http_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/interfaces_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/interfaces_test_php5_1.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/live_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/mock_objects_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/page_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/parse_error_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/parser_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/reflection_php4_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/reflection_php5_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/remote_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/shell_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/shell_tester_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/simpletest_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/.htaccess create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/1.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/2.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/3.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_change_redirect.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_tag/base_link.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_tag/form.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_tag/frameset.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_tag/frameset_with_base_tag.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_tag/page_1.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_tag/page_2.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/base_tag/relative_link.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/cookie_based_counter.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/counting_frameset.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/double_base_change_redirect.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/file.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_data_encoded_form.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_with_array_based_inputs.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_with_false_defaults.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_with_mixed_post_and_get.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_with_quoted_values.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_with_radio_buttons.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_with_tricky_defaults.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_with_unnamed_submit.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/form_without_action.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/frame_a.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/frame_b.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/frame_links.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/frameset.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/front_controller_style/a_page.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/front_controller_style/index.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/front_controller_style/show_request.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/link_confirm.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/local_redirect.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/messy_frameset.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/multiple_widget_form.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/nested_frameset.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/network_confirm.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/one_page_frameset.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/page_request.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/path/base_change_redirect.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/path/network_confirm.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/path/show_cookies.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/.htaccess create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/.htpasswd create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/1.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/2.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/3.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/htaccess create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/local_redirect.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/protected/network_confirm.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/redirect.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/savant_style_form.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/search.png create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/self.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/self_form.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/set_cookies.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/slow_page.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/temp/.stop_cvs_removing_temp create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/timestamp.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/upload_form.html create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/site/upload_handler.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/socket_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/collector/collectable.1 create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/collector/collectable.2 create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/empty_test_file.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/latin1_sample create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/spl_examples.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/supplementary_upload_sample.txt create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/test1.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/support/upload_sample.txt create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/tag_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/unit_tester_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/unit_tests.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/url_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/user_agent_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/utf8_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/visual/visual_errors.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/visual_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/web_tester_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test/xml_test.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/test_case.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/Expectations.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/FormTesting.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/GroupTests.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/MockObjects.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/PartialMock.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/Reporting.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/ServerStubs.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/SimpleTest.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/SimpleTest.pkg.ini create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/UnitTestCase.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/tutorials/SimpleTest/WebTester.pkg create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/unit_tester.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/url.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/user_agent.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/web_tester.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/simpletest/xml.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Action.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Actions/CallbackAction.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Actions/ReturnReferenceAction.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Actions/ReturnValueAction.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Actions/ThrowAction.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Description.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Expectation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/ExpectationProvider.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Expectations.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Expectations/AbstractExpectation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Expectations/AtLeastExpectation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Expectations/AtMostExpectation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Expectations/BetweenExpectation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Expectations/ExactlyExpectation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Invocation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/InvocationHandler.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/InvocationProxy.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/InvocationRecorder.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matchers/AnyMatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matchers/BoundsMatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matchers/EqualMatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matchers/IdenticalMatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matchers/OptionalMatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matchers/PatternMatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Matchers/ReferenceMatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/MockGenerator.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/MockObject.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Mockery.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/NotSatisfiedException.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/SelfDescribing.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/Sequence.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/SimpleDescription.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/SimpleInvocation.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/SimpleSequence.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/SimpleState.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/SimpleStatePredicate.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/State.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/StateMachine.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/StatePredicate.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/classes/Yay/States.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/mock.tpl.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/yay_convenience.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/lib/yaymock/yay_mock.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/run.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/sweety.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/css/main.css create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/darr.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/group.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/htmlicon.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/loading.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/network.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/rarr.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/runicon.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/images/xmlicon.gif create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/js/sweety-template.js create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/suite-ui-noajax.tpl.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/templates/sweety/suite-ui.tpl.php create mode 100644 vendor/swiftmailer/swiftmailer/test-suite/xpath-legacy.js create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-2022-jp/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-8859-1/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/three.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/two.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.priv create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.pub create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/data.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.p12 create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.csr create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.p12 create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance.conf.php.default create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/AttachmentAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/ByteStream/FileByteStreamAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Base64EncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/helpers/Swift/Tests/IdenticalBinaryExpectation.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/helpers/Swift/Tests/SwiftSmokeTestCase.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/helpers/Swift/Tests/SwiftUnitTestCase.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/AttachmentSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/AntiFloodPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..21256661 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..49d6cd59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Numerous always-ignore extensions +*.diff +*.err +*.orig +*.log +*.rej +*.swo +*.swp +*.vi +*~ +*.sass-cache + +# OS or Editor folders +.DS_Store +Thumbs.db +.cache +.project +.settings +.tmproj +*.esproj +nbproject + +# Dreamweaver added files +_notes +dwsync.xml + +# Komodo +*.komodoproject +.komodotools + +# Folders to ignore +.hg +.svn +.CVS +intermediate +publish +.idea \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..015febc4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contribution Guidelines + +Please submit all issues and pull requests to the [laravel/framework](http://github.com/laravel/framework) repository! \ No newline at end of file diff --git a/app/commands/.gitkeep b/app/commands/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/config/app.php b/app/config/app.php new file mode 100644 index 00000000..20498393 --- /dev/null +++ b/app/config/app.php @@ -0,0 +1,184 @@ + false, + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => 'http://pony.fm', + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, long string, otherwise these encrypted values will not + | be safe. Make sure to change it before deploying any application! + | + */ + + 'key' => 'YourSecretKey!!!', + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => array( + + 'Illuminate\Foundation\Providers\ArtisanServiceProvider', + 'Illuminate\Auth\AuthServiceProvider', + 'Illuminate\Cache\CacheServiceProvider', + 'Illuminate\Foundation\Providers\CommandCreatorServiceProvider', + 'Illuminate\Session\CommandsServiceProvider', + 'Illuminate\Foundation\Providers\ComposerServiceProvider', + 'Illuminate\Routing\ControllerServiceProvider', + 'Illuminate\Cookie\CookieServiceProvider', + 'Illuminate\Database\DatabaseServiceProvider', + 'Illuminate\Encryption\EncryptionServiceProvider', + 'Illuminate\Filesystem\FilesystemServiceProvider', + 'Illuminate\Hashing\HashServiceProvider', + 'Illuminate\Html\HtmlServiceProvider', + 'Illuminate\Foundation\Providers\KeyGeneratorServiceProvider', + 'Illuminate\Log\LogServiceProvider', + 'Illuminate\Mail\MailServiceProvider', + 'Illuminate\Foundation\Providers\MaintenanceServiceProvider', + 'Illuminate\Database\MigrationServiceProvider', + 'Illuminate\Foundation\Providers\OptimizeServiceProvider', + 'Illuminate\Pagination\PaginationServiceProvider', + 'Illuminate\Foundation\Providers\PublisherServiceProvider', + 'Illuminate\Queue\QueueServiceProvider', + 'Illuminate\Redis\RedisServiceProvider', + 'Illuminate\Auth\Reminders\ReminderServiceProvider', + 'Illuminate\Foundation\Providers\RouteListServiceProvider', + 'Illuminate\Database\SeedServiceProvider', + 'Illuminate\Foundation\Providers\ServerServiceProvider', + 'Illuminate\Session\SessionServiceProvider', + 'Illuminate\Foundation\Providers\TinkerServiceProvider', + 'Illuminate\Translation\TranslationServiceProvider', + 'Illuminate\Validation\ValidationServiceProvider', + 'Illuminate\View\ViewServiceProvider', + 'Illuminate\Workbench\WorkbenchServiceProvider', + + ), + + /* + |-------------------------------------------------------------------------- + | Service Provider Manifest + |-------------------------------------------------------------------------- + | + | The service provider manifest is used by Laravel to lazy load service + | providers which are not needed for each request, as well to keep a + | list of all of the services. Here, you may set its storage spot. + | + */ + + 'manifest' => storage_path().'/meta', + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + + 'aliases' => array( + + 'App' => 'Illuminate\Support\Facades\App', + 'Artisan' => 'Illuminate\Support\Facades\Artisan', + 'Auth' => 'Illuminate\Support\Facades\Auth', + 'Blade' => 'Illuminate\Support\Facades\Blade', + 'Cache' => 'Illuminate\Support\Facades\Cache', + 'ClassLoader' => 'Illuminate\Support\ClassLoader', + 'Config' => 'Illuminate\Support\Facades\Config', + 'Controller' => 'Illuminate\Routing\Controllers\Controller', + 'Cookie' => 'Illuminate\Support\Facades\Cookie', + 'Crypt' => 'Illuminate\Support\Facades\Crypt', + 'DB' => 'Illuminate\Support\Facades\DB', + 'Eloquent' => 'Illuminate\Database\Eloquent\Model', + 'Event' => 'Illuminate\Support\Facades\Event', + 'File' => 'Illuminate\Support\Facades\File', + 'Form' => 'Illuminate\Support\Facades\Form', + 'Hash' => 'Illuminate\Support\Facades\Hash', + 'HTML' => 'Illuminate\Support\Facades\HTML', + 'Input' => 'Illuminate\Support\Facades\Input', + 'Lang' => 'Illuminate\Support\Facades\Lang', + 'Log' => 'Illuminate\Support\Facades\Log', + 'Mail' => 'Illuminate\Support\Facades\Mail', + 'Paginator' => 'Illuminate\Support\Facades\Paginator', + 'Password' => 'Illuminate\Support\Facades\Password', + 'Queue' => 'Illuminate\Support\Facades\Queue', + 'Redirect' => 'Illuminate\Support\Facades\Redirect', + 'Redis' => 'Illuminate\Support\Facades\Redis', + 'Request' => 'Illuminate\Support\Facades\Request', + 'Response' => 'Illuminate\Support\Facades\Response', + 'Route' => 'Illuminate\Support\Facades\Route', + 'Schema' => 'Illuminate\Support\Facades\Schema', + 'Seeder' => 'Illuminate\Database\Seeder', + 'Session' => 'Illuminate\Support\Facades\Session', + 'Str' => 'Illuminate\Support\Str', + 'URL' => 'Illuminate\Support\Facades\URL', + 'Validator' => 'Illuminate\Support\Facades\Validator', + 'View' => 'Illuminate\Support\Facades\View', + + ), + +); diff --git a/app/config/auth.php b/app/config/auth.php new file mode 100644 index 00000000..79c15f02 --- /dev/null +++ b/app/config/auth.php @@ -0,0 +1,63 @@ + 'pfm', + + /* + |-------------------------------------------------------------------------- + | Authentication Model + |-------------------------------------------------------------------------- + | + | When using the "Eloquent" authentication driver, we need to know which + | Eloquent model should be used to retrieve your users. Of course, it + | is often just the "User" model but you may use whatever you like. + | + */ + + 'model' => 'User', + + /* + |-------------------------------------------------------------------------- + | Authentication Table + |-------------------------------------------------------------------------- + | + | When using the "Database" authentication driver, we need to know which + | table should be used to retrieve your users. We have chosen a basic + | default value but you may easily change it to any table you like. + | + */ + + 'table' => 'users', + + /* + |-------------------------------------------------------------------------- + | Password Reminder Settings + |-------------------------------------------------------------------------- + | + | Here you may set the settings for password reminders, including a view + | that should be used as your password reminder e-mail. You will also + | be able to set the name of the table that holds the reset tokens. + | + */ + + 'reminder' => array( + + 'email' => 'emails.auth.reminder', 'table' => 'password_reminders', + + ), + +); \ No newline at end of file diff --git a/app/config/cache.php b/app/config/cache.php new file mode 100644 index 00000000..ce898423 --- /dev/null +++ b/app/config/cache.php @@ -0,0 +1,89 @@ + 'file', + + /* + |-------------------------------------------------------------------------- + | File Cache Location + |-------------------------------------------------------------------------- + | + | When using the "file" cache driver, we need a location where the cache + | files may be stored. A sensible default has been specified, but you + | are free to change it to any other place on disk that you desire. + | + */ + + 'path' => storage_path().'/cache', + + /* + |-------------------------------------------------------------------------- + | Database Cache Connection + |-------------------------------------------------------------------------- + | + | When using the "database" cache driver you may specify the connection + | that should be used to store the cached items. When this option is + | null the default database connection will be utilized for cache. + | + */ + + 'connection' => null, + + /* + |-------------------------------------------------------------------------- + | Database Cache Table + |-------------------------------------------------------------------------- + | + | When using the "database" cache driver we need to know the table that + | should be used to store the cached items. A default table name has + | been provided but you're free to change it however you deem fit. + | + */ + + 'table' => 'cache', + + /* + |-------------------------------------------------------------------------- + | Memcached Servers + |-------------------------------------------------------------------------- + | + | Now you may specify an array of your Memcached servers that should be + | used when utilizing the Memcached cache driver. All of the servers + | should contain a value for "host", "port", and "weight" options. + | + */ + + 'memcached' => array( + + array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), + + ), + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing a RAM based store such as APC or Memcached, there might + | be other applications utilizing the same cache. So, we'll specify a + | value to get prefixed to all our keys so we can avoid collisions. + | + */ + + 'prefix' => 'laravel', + +); diff --git a/app/config/compile.php b/app/config/compile.php new file mode 100644 index 00000000..54d7185b --- /dev/null +++ b/app/config/compile.php @@ -0,0 +1,18 @@ + PDO::FETCH_CLASS, + + /* + |-------------------------------------------------------------------------- + | Default Database Connection Name + |-------------------------------------------------------------------------- + | + | Here you may specify which of the database connections below you wish + | to use as your default connection for all database work. Of course + | you may use many connections at once using the Database library. + | + */ + + 'default' => 'mysql', + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => array( + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => __DIR__.'/../database/production.sqlite', + 'prefix' => '', + ), + + 'mysql' => array( + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + ), + + 'pgsql' => array( + 'driver' => 'pgsql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'prefix' => '', + 'schema' => 'public', + ), + + 'sqlsrv' => array( + 'driver' => 'sqlsrv', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'prefix' => '', + ), + + ), + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk have not actually be run in the databases. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer set of commands than a typical key-value systems + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => array( + + 'cluster' => true, + + 'default' => array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0, + ), + + ), + +); diff --git a/app/config/local/.gitignore b/app/config/local/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/app/config/local/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/app/config/mail.php b/app/config/mail.php new file mode 100644 index 00000000..eb9e6406 --- /dev/null +++ b/app/config/mail.php @@ -0,0 +1,111 @@ + 'smtp', + + /* + |-------------------------------------------------------------------------- + | SMTP Host Address + |-------------------------------------------------------------------------- + | + | Here you may provide the host address of the SMTP server used by your + | applications. A default option is provided that is compatible with + | the Postmark mail service, which will provide reliable delivery. + | + */ + + 'host' => 'smtp.mailgun.org', + + /* + |-------------------------------------------------------------------------- + | SMTP Host Port + |-------------------------------------------------------------------------- + | + | This is the SMTP port used by your application to delivery e-mails to + | users of your application. Like the host we have set this value to + | stay compatible with the Postmark e-mail application by default. + | + */ + + 'port' => 587, + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => array('address' => null, 'name' => null), + + /* + |-------------------------------------------------------------------------- + | E-Mail Encryption Protocol + |-------------------------------------------------------------------------- + | + | Here you may specify the encryption protocol that should be used when + | the application send e-mail messages. A sensible default using the + | transport layer security protocol should provide great security. + | + */ + + 'encryption' => 'tls', + + /* + |-------------------------------------------------------------------------- + | SMTP Server Username + |-------------------------------------------------------------------------- + | + | If your SMTP server requires a username for authentication, you should + | set it here. This will get used to authenticate with your server on + | connection. You may also set the "password" value below this one. + | + */ + + 'username' => null, + + /* + |-------------------------------------------------------------------------- + | SMTP Server Password + |-------------------------------------------------------------------------- + | + | Here you may set the password required by your SMTP server to send out + | messages from your application. This will be given to the server on + | connection so that the application will be able to send messages. + | + */ + + 'password' => null, + + /* + |-------------------------------------------------------------------------- + | Sendmail System Path + |-------------------------------------------------------------------------- + | + | When using the "sendmail" driver to send e-mails, we will need to know + | the path to where Sendmail lives on this server. A default path has + | been provided here, which will work well on most of your systems. + | + */ + + 'sendmail' => '/usr/sbin/sendmail -bs', + +); diff --git a/app/config/packages/.gitkeep b/app/config/packages/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/config/queue.php b/app/config/queue.php new file mode 100644 index 00000000..220998cb --- /dev/null +++ b/app/config/queue.php @@ -0,0 +1,60 @@ + 'sync', + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + */ + + 'connections' => array( + + 'sync' => array( + 'driver' => 'sync', + ), + + 'beanstalkd' => array( + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + ), + + 'sqs' => array( + 'driver' => 'sqs', + 'key' => 'your-public-key', + 'secret' => 'your-secret-key', + 'queue' => 'your-queue-url', + 'region' => 'us-east-1', + ), + + 'iron' => array( + 'driver' => 'iron', + 'project' => 'your-project-id', + 'token' => 'your-token', + 'queue' => 'your-queue-name', + ), + + ), + +); diff --git a/app/config/session.php b/app/config/session.php new file mode 100644 index 00000000..e11e98cd --- /dev/null +++ b/app/config/session.php @@ -0,0 +1,125 @@ + 'native', + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle for it is expired. If you want them + | to immediately expire when the browser closes, set it to zero. + | + */ + + 'lifetime' => 120, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path().'/sessions', + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the database + | connection that should be used to manage your sessions. This should + | correspond to a connection in your "database" configuration file. + | + */ + + 'connection' => null, + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => array(2, 100), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => 'laravel_session', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => null, + +); diff --git a/app/config/testing/cache.php b/app/config/testing/cache.php new file mode 100644 index 00000000..16d3ae2f --- /dev/null +++ b/app/config/testing/cache.php @@ -0,0 +1,20 @@ + 'array', + +); \ No newline at end of file diff --git a/app/config/testing/session.php b/app/config/testing/session.php new file mode 100644 index 00000000..a18c1b9f --- /dev/null +++ b/app/config/testing/session.php @@ -0,0 +1,21 @@ + 'array', + +); \ No newline at end of file diff --git a/app/config/view.php b/app/config/view.php new file mode 100644 index 00000000..eba10a4c --- /dev/null +++ b/app/config/view.php @@ -0,0 +1,31 @@ + array(__DIR__.'/../views'), + + /* + |-------------------------------------------------------------------------- + | Pagination View + |-------------------------------------------------------------------------- + | + | This view will be used to render the pagination link output, and can + | be easily customized here to show any view you like. A clean view + | compatible with Twitter's Bootstrap is given to you by default. + | + */ + + 'pagination' => 'pagination::slider', + +); diff --git a/app/config/workbench.php b/app/config/workbench.php new file mode 100644 index 00000000..56bee526 --- /dev/null +++ b/app/config/workbench.php @@ -0,0 +1,31 @@ + '', + + /* + |-------------------------------------------------------------------------- + | Workbench Author E-Mail Address + |-------------------------------------------------------------------------- + | + | Like the option above, your e-mail address is used when generating new + | workbench packages. The e-mail is placed in your composer.json file + | automatically after the package is created by the workbench tool. + | + */ + + 'email' => '', + +); \ No newline at end of file diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php new file mode 100644 index 00000000..f7f46edb --- /dev/null +++ b/app/controllers/AccountController.php @@ -0,0 +1,7 @@ + \Input::get('email'), 'password' => \Input::get('password')), \Input::get('remember'))) + return \Response::json(['messages' => ['username' => 'Invalid username or password']], 400); + + return \Response::json(['user' => \Auth::user()]); + } + + public function postLogout() { + \Auth::logout(); + } + + public function postRegister() { + $command = new RegisterUserCommand(); + if (!$command->authorize()) + return \Response::json([], 403); + + $errors = $command->validate(); + if ($errors->fails()) + return \Response::json([], 400); + + return \Response::json($command->execute()); + } + } \ No newline at end of file diff --git a/app/controllers/Api/Web/TaxonomiesController.php b/app/controllers/Api/Web/TaxonomiesController.php new file mode 100644 index 00000000..717e1102 --- /dev/null +++ b/app/controllers/Api/Web/TaxonomiesController.php @@ -0,0 +1,17 @@ + License::all()->toArray(), + 'genres' => Genre::orderBy('name')->get()->toArray(), + 'track_types' => TrackType::all()->toArray() + ], 200); + } + } \ No newline at end of file diff --git a/app/controllers/Api/Web/TracksController.php b/app/controllers/Api/Web/TracksController.php new file mode 100644 index 00000000..4a67b658 --- /dev/null +++ b/app/controllers/Api/Web/TracksController.php @@ -0,0 +1,99 @@ +execute(new UploadTrackCommand()); + } + + public function getOwned() { + $query = Track::summary()->whereNull('deleted_at')->where('user_id', \Auth::user()->id); + + if (\Input::has('published')) { + $published = \Input::get('published'); + if ($published) + $query->whereNotNull('published_at'); + else + $query->whereNull('published_at'); + } + + if (\Input::has('order')) { + $order = \Input::get('order'); + $parts = explode(',', $order); + $query->orderBy($parts[0], $parts[1]); + } + + if (\Input::has('genres')) + $query->whereIn('genre_id', \Input::get('genres')); + + if (\Input::has('types')) + $query->whereIn('track_type_id', \Input::get('types')); + + $dbTracks = $query->get(); + $tracks = []; + + foreach ($dbTracks as $track) { + $tracks[] = [ + 'id' => $track->id, + 'title' => $track->title, + 'user_id' => $track->user_id, + 'slug' => $track->slug, + 'is_vocal' => $track->is_vocal, + 'is_explicit' => $track->is_explicit, + 'is_downloadable' => $track->is_downloadable, + 'is_published' => $track->published_at != null, + 'created_at' => $track->created_at, + 'published_at' => $track->published_at, + 'duration' => $track->duration, + 'genre_id' => $track->genre_id, + 'track_type_id' => $track->track_type_id, + ]; + } + + return \Response::json($tracks, 200); + } + + public function getEdit($id) { + $track = Track::find($id); + if (!$track) + return $this->notFound('Track ' . $id . ' not found!'); + + if ($track->user_id != \Auth::user()->id) + return $this->notAuthorized(); + + return \Response::json([ + 'id' => $track->id, + 'title' => $track->title, + 'user_id' => $track->user_id, + 'slug' => $track->slug, + 'is_vocal' => (bool)$track->is_vocal, + 'is_explicit' => (bool)$track->is_explicit, + 'is_downloadable' => $track->published_at == null ? true : (bool)$track->is_downloadable, + 'is_published' => $track->published_at != null, + 'created_at' => $track->created_at, + 'published_at' => $track->published_at, + 'duration' => $track->duration, + 'genre_id' => $track->genre_id, + 'track_type_id' => $track->track_type_id, + 'license_id' => $track->license_id != null ? $track->license_id : 3, + 'description' => $track->description, + 'lyrics' => $track->lyrics, + 'released_at' => $track->released_at + ], 200); + } + + public function postDelete($id) { + return $this->execute(new DeleteTrackCommand($id)); + } + + public function putEdit($id) { + return $this->execute(new EditTrackCommand($id, \Input::all())); + } + } \ No newline at end of file diff --git a/app/controllers/ApiControllerBase.php b/app/controllers/ApiControllerBase.php new file mode 100644 index 00000000..0553f885 --- /dev/null +++ b/app/controllers/ApiControllerBase.php @@ -0,0 +1,23 @@ +authorize()) + return $this->notAuthorized(); + + $result = $command->execute(); + if ($result->didFail()) { + return Response::json(['message' => 'Validation failed', 'errors' => $result->getValidator()->messages()->getMessages()], 400); + } + + return Response::json($result->getResponse(), 200); + } + + public function notAuthorized() { + return Response::json(['message' => 'You may not do this!'], 403); + } + + public function notFound($message) { + return Response::json(['message' => $message], 403); + } + } \ No newline at end of file diff --git a/app/controllers/ArtistsController.php b/app/controllers/ArtistsController.php new file mode 100644 index 00000000..e0bc496c --- /dev/null +++ b/app/controllers/ArtistsController.php @@ -0,0 +1,7 @@ +increments('id'); + $table->string('display_name', 255); + $table->string('mlpforums_name')->nullable(); + $table->boolean('sync_names')->default(true); + $table->string('password_hash', 32); + $table->string('password_salt', 5); + $table->string('email', 150)->unique(); + $table->string('gravatar')->nullable(); + $table->string('slug'); + $table->boolean('uses_gravatar')->default(true); + $table->boolean('can_see_explicit_content'); + $table->text('bio'); + $table->timestamps(); + }); + + Schema::create('roles', function($table){ + $table->increments('id'); + $table->string('name'); + }); + + Schema::create('role_user', function($table){ + $table->increments('id'); + $table->integer('user_id')->unsigned()->index(); + $table->integer('role_id')->unsigned()->index(); + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('role_id')->references('id')->on('roles'); + }); + + Schema::create('cache', function($table){ + $table->string('key')->index(); + $table->text('value'); + $table->integer('expiration')->unsigned()->index(); + }); + + DB::table('roles')->insert(['name' => 'super_admin']); + DB::table('roles')->insert(['name' => 'admin']); + DB::table('roles')->insert(['name' => 'moderator']); + DB::table('roles')->insert(['name' => 'user']); + } + + public function down() { + Schema::drop('cache'); + Schema::drop('role_user'); + Schema::drop('roles'); + Schema::drop('users'); + } +} diff --git a/app/database/migrations/2013_06_27_015259_create_tracks_table.php b/app/database/migrations/2013_06_27_015259_create_tracks_table.php new file mode 100644 index 00000000..7952a93d --- /dev/null +++ b/app/database/migrations/2013_06_27_015259_create_tracks_table.php @@ -0,0 +1,117 @@ +increments('id'); + $table->string('title', 100); + $table->text('description'); + $table->boolean('affiliate_distribution'); + $table->boolean('open_distribution'); + $table->boolean('remix'); + }); + + Schema::create('genres', function($table){ + $table->increments('id'); + $table->string('name')->unique(); + $table->string('slug', 200)->index(); + }); + + Schema::create('track_types', function($table){ + $table->increments('id'); + $table->string('title'); + $table->string('editor_title'); + }); + + Schema::create('tracks', function($table){ + $table->increments('id'); + + $table->integer('user_id')->unsigned(); + $table->integer('license_id')->unsigned()->nullable()->default(NULL); + $table->integer('genre_id')->unsigned()->nullable()->index()->default(NULL); + $table->integer('track_type_id')->unsigned()->nullable()->default(NULL); + + $table->string('title', 100)->index(); + $table->string('slug', 200)->index(); + $table->text('description')->nullable(); + $table->text('lyrics')->nullable(); + $table->boolean('is_vocal'); + $table->boolean('is_explicit'); + $table->integer('cover_id')->nullable()->unsigned()->default(NULL); + $table->boolean('is_downloadable'); + $table->float('duration')->unsigned(); + + $table->timestamps(); + $table->timestamp('deleted_at')->nullable()->index(); + $table->timestamp('published_at')->nullable()->index(); + $table->timestamp('released_at')->nullable(); + + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('license_id')->references('id')->on('licenses'); + $table->foreign('genre_id')->references('id')->on('genres')->on_update('cascade'); + $table->foreign('track_type_id')->references('id')->on('track_types')->on_update('cascade'); + }); + + + DB::table('licenses')->insert([ + 'title' => 'Personal', + 'description' => 'Only you and Pony.fm are allowed to distribute and broadcast the track.', + 'affiliate_distribution' => 0, + 'open_distribution' => 0, + 'remix' => 0 + ]); + + DB::table('licenses')->insert([ + 'title' => 'Broadcast', + 'description' => 'You, Pony.fm, and its affiliates may distribute and broadcast the track.', + 'affiliate_distribution' => 1, + 'open_distribution' => 0, + 'remix' => 0 + ]); + + DB::table('licenses')->insert([ + 'title' => 'Open', + 'description' => 'Anyone is permitted to broadcast and distribute the song in its original form, with attribution to you.', + 'affiliate_distribution' => 1, + 'open_distribution' => 1, + 'remix' => 0 + ]); + + DB::table('licenses')->insert([ + 'title' => 'Remix', + 'description' => 'Anyone is permitted to broadcast and distribute the song in any form, or create derivative works based on it for any purpose, with attribution to you.', + 'affiliate_distribution' => 1, + 'open_distribution' => 1, + 'remix' => 1 + ]); + + DB::table('track_types')->insert([ + 'title' => 'Original Song', + 'editor_title' => 'an original song' + ]); + + DB::table('track_types')->insert([ + 'title' => 'Official Song Remix', + 'editor_title' => 'a remix of an official song' + ]); + + DB::table('track_types')->insert([ + 'title' => 'Fan Song Remix', + 'editor_title' => 'a remix of a fan song' + ]); + + DB::table('track_types')->insert([ + 'title' => 'Ponified Song', + 'editor_title' => 'a non-pony song, turned pony' + ]); + } + + public function down() { + Schema::drop('tracks'); + Schema::drop('licenses'); + Schema::drop('track_types'); + Schema::drop('genres'); + } +} \ No newline at end of file diff --git a/app/database/production.sqlite b/app/database/production.sqlite new file mode 100644 index 00000000..e69de29b diff --git a/app/database/seeds/.gitkeep b/app/database/seeds/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/database/seeds/DatabaseSeeder.php b/app/database/seeds/DatabaseSeeder.php new file mode 100644 index 00000000..6a8c204c --- /dev/null +++ b/app/database/seeds/DatabaseSeeder.php @@ -0,0 +1,17 @@ +call('UserTableSeeder'); + } + +} \ No newline at end of file diff --git a/app/filters.php b/app/filters.php new file mode 100644 index 00000000..9cd091cc --- /dev/null +++ b/app/filters.php @@ -0,0 +1,79 @@ + '« Previous', + + 'next' => 'Next »', + +); \ No newline at end of file diff --git a/app/lang/en/reminders.php b/app/lang/en/reminders.php new file mode 100644 index 00000000..4a9f1766 --- /dev/null +++ b/app/lang/en/reminders.php @@ -0,0 +1,22 @@ + "Passwords must be six characters and match the confirmation.", + + "user" => "We can't find a user with that e-mail address.", + + "token" => "This password reset token is invalid.", + +); \ No newline at end of file diff --git a/app/lang/en/validation.php b/app/lang/en/validation.php new file mode 100644 index 00000000..40d4d727 --- /dev/null +++ b/app/lang/en/validation.php @@ -0,0 +1,104 @@ + "The :attribute must be accepted.", + "active_url" => "The :attribute is not a valid URL.", + "after" => "The :attribute must be a date after :date.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "before" => "The :attribute must be a date before :date.", + "between" => array( + "numeric" => "The :attribute must be between :min - :max.", + "file" => "The :attribute must be between :min - :max kilobytes.", + "string" => "The :attribute must be between :min - :max characters.", + ), + "confirmed" => "The :attribute confirmation does not match.", + "date" => "The :attribute is not a valid date.", + "date_format" => "The :attribute does not match the format :format.", + "different" => "The :attribute and :other must be different.", + "digits" => "The :attribute must be :digits digits.", + "digits_between" => "The :attribute must be between :min and :max digits.", + "email" => "The :attribute format is invalid.", + "exists" => "The selected :attribute is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "ip" => "The :attribute must be a valid IP address.", + "max" => array( + "numeric" => "The :attribute may not be greater than :max.", + "file" => "The :attribute may not be greater than :max kilobytes.", + "string" => "The :attribute may not be greater than :max characters.", + ), + "mimes" => "The :attribute must be a file of type: :values.", + "min" => array( + "numeric" => "The :attribute must be at least :min.", + "file" => "The :attribute must be at least :min kilobytes.", + "string" => "The :attribute must be at least :min characters.", + ), + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "regex" => "The :attribute format is invalid.", + "required" => "The :attribute field is required.", + "required_if" => "The :attribute field is required when :other is :value.", + "required_with" => "The :attribute field is required when :values is present.", + "required_without" => "The :attribute field is required when :values is not present.", + "same" => "The :attribute and :other must match.", + "size" => array( + "numeric" => "The :attribute must be :size.", + "file" => "The :attribute must be :size kilobytes.", + "string" => "The :attribute must be :size characters.", + ), + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array(), + + //=== CUSTOM VALIDATION MESSAGES ===// + + "audio" => "The :attribute must be an audio file.", + "audio_channels" => "The :attribute contains an invalid number of channels.", + "audio_format" => "The :attribute does not contain audio in a valid format.", + "required_when" => "The :attribute field cannot be left blank.", + "sample_rate" => "The :attribute has an invalid sample rate.", + "min_width" => "The :attribute is not wide enough.", + "min_height" => "The :attribute is not tall enough.", + "textarea_length" => "The :attribute must be less than 250 characters long.", // @TODO: Figure out how to retrieve the parameter from the validation rule + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); diff --git a/app/library/Assets.php b/app/library/Assets.php new file mode 100644 index 00000000..2d3e0361 --- /dev/null +++ b/app/library/Assets.php @@ -0,0 +1,81 @@ +getSourceRoot() . '/' . $script->getSourcePath() . '?' . gmdate($js->getLastModified()) . '">'; + } + + return $retVal; + } + + return ''; + } + + public static function styleIncludes($area = 'app') { + $css = self::styleAssetCollection($area); + + if (Config::get('app.debug')) { + $retVal = ''; + + foreach ($css as $style) { + if ($style instanceof CacheBusterAsset) + continue; + + $retVal .= ''; + } + + return $retVal; + } + + return ''; + } + + public static function scriptAssetCollection($area) { + if ($area == 'app') + return new AssetCollection([ + new FileAsset('scripts/base/jquery-2.0.2.js'), + new FileAsset('scripts/base/underscore.js'), + new FileAsset('scripts/base/angular.js'), + new FileAsset('scripts/base/ui-bootstrap-tpls-0.4.0.js'), + new FileAsset('scripts/base/angular-ui-router.js'), + new AssetCollection([ + new GlobAsset('scripts/shared/*.coffee'), + new GlobAsset('scripts/app/*.coffee'), + new GlobAsset('scripts/app/services/*.coffee'), + new GlobAsset('scripts/app/filters/*.coffee'), + new GlobAsset('scripts/app/filters/*.js'), + new GlobAsset('scripts/app/directives/*.coffee'), + new GlobAsset('scripts/app/controllers/*.coffee'), + ], [ + new CoffeeScriptFilter(Config::get('app.coffee')) + ]) + ]); + + throw new Exception(); + } + + public static function styleAssetCollection($area) { + if ($area == 'app') { + $lastModifiedCollection = new AssetCollection([new GlobAsset("styles/*.less")]); + + $css = new AssetCollection([ + new FileAsset('styles/app.less'), + new CacheBusterAsset($lastModifiedCollection->getLastModified()) + ], [new \Assetic\Filter\LessFilter('node')]); + + return $css; + } + + throw new Exception(); + } + } \ No newline at end of file diff --git a/app/library/AudioCache.php b/app/library/AudioCache.php new file mode 100644 index 00000000..48866988 --- /dev/null +++ b/app/library/AudioCache.php @@ -0,0 +1,16 @@ +_lastModified = $lastModified; + parent::__construct([], '', '', []); + } + + public function load(FilterInterface $additionalFilter = null) { + } + + public function getLastModified() { + return $this->_lastModified; + } + } diff --git a/app/library/Helpers.php b/app/library/Helpers.php new file mode 100644 index 00000000..96fb1c1d --- /dev/null +++ b/app/library/Helpers.php @@ -0,0 +1,7 @@ + $options['salt']]) === $hashedValue; + } + + public function needsRehash($hashedValue, array $options = array()) { + return false; + } + + static public function ips_sanitize( $value ) { + $value = str_replace('&', '&', $value); + $value = str_replace('\\', '\', $value); + $value = str_replace('!', '!', $value); + $value = str_replace('$', '$', $value); + $value = str_replace('"', '"', $value); + $value = str_replace('<', '<', $value); + $value = str_replace('>', '>', $value); + $value = str_replace('\'', ''', $value); + return $value; + } + } \ No newline at end of file diff --git a/app/library/PfmAuth.php b/app/library/PfmAuth.php new file mode 100644 index 00000000..277a65a0 --- /dev/null +++ b/app/library/PfmAuth.php @@ -0,0 +1,15 @@ +hasher->check($plain, $user->getAuthPassword(), ['salt' => $user->password_salt]); + } + } \ No newline at end of file diff --git a/app/library/PfmValidator.php b/app/library/PfmValidator.php new file mode 100644 index 00000000..aa1df4ad --- /dev/null +++ b/app/library/PfmValidator.php @@ -0,0 +1,147 @@ +getPathname()); + return in_array($file->getAudioCodec(), $parameters); + } + + + /** + * Validate the sample rate of the audio file. + * + * @param string $attribute + * @param array $value + * @param array $parameters + * @return bool + */ + public function validateSampleRate($attribute, $value, $parameters) + { + // attribute is the file field + // value is the file array itself + // parameters is a list of sample rates the file can be, verified via ffmpeg + $file = AudioCache::get($value->getPathname()); + return in_array($file->getAudioSampleRate(), $parameters); + } + + + /** + * Validate the number of channels in the audio file. + * + * @param string $attribute + * @param array $value + * @param array $parameters + * @return bool + */ + public function validateAudioChannels($attribute, $value, $parameters) + { + // attribute is the file field + // value is the file array itself + // parameters is a list of sample rates the file can be, verified via ffmpeg + $file = AudioCache::get($value->getPathname()); + return in_array($file->getAudioChannels(), $parameters); + } + + + /** + * Validate the bit rate of the audio file. + * + * @param string $attribute + * @param array $value + * @param array $parameters + * @return bool + */ + public function validateAudioBitrate($attribute, $value, $parameters) + { + // attribute is the file field + // value is the file array itself + // parameters is a list of sample rates the file can be, verified via ffmpeg + $file = AudioCache::get($value->getPathname()); + return in_array($file->getAudioBitRate(), $parameters); + } + + + /** + * Validate the duration of the audio file, in seconds. + * + * @param string $attribute + * @param array $value + * @param array $parameters + * @return bool + */ + public function validateMinDuration($attribute, $value, $parameters) + { + // attribute is the file field + // value is the file array itself + // parameters is an array containing one value: the minimum duration + $file = AudioCache::get($value->getPathname()); + return $file->getDuration() >= (float) $parameters[0]; + } + + + /** + * Require a field when the value of another field matches a certain value. + * + * @param string $attribute + * @param array $value + * @param array $parameters + * @return bool + */ + /** OLD CODE + public function validate_required_when($attribute, $value, $parameters) + { + if ( Input::get($parameters[0]) === $parameters[1] && static::required($attribute, $value) ){ + return true; + + } else { + return false; + } + } + **/ + + // custom required_when validator + public function validateRequiredWhen($attribute, $value, $parameters){ + if ( Input::get($parameters[0]) == $parameters[1] ) { + return $this->validate_required($attribute, $value); + } + + return true; + } + + + // custom image width validator + public function validateMinWidth($attribute, $value, $parameters){ + return getimagesize($value->getPathname())[0] >= $parameters[0]; + } + + // custom image height validator + public function validateMinHeight($attribute, $value, $parameters){ + return getimagesize($value->getPathname())[1] >= $parameters[0]; + } + + public function validateTextareaLength($attribute, $value, $parameters) { + return strlen(str_replace("\r\n", "\n", $value)) <= $parameters[0]; + } + } \ No newline at end of file diff --git a/app/models/Commands/CommandBase.php b/app/models/Commands/CommandBase.php new file mode 100644 index 00000000..6776c0d7 --- /dev/null +++ b/app/models/Commands/CommandBase.php @@ -0,0 +1,29 @@ +_listeners[] = $listener; + } + + protected function notify($message, $progress) { + foreach ($this->_listeners as $listener) { + $listener($message, $progress); + } + } + + /** + * @return bool + */ + public function authorize() { + return true; + } + + /** + * @return CommandResponse + */ + public abstract function execute(); + } \ No newline at end of file diff --git a/app/models/Commands/CommandResponse.php b/app/models/Commands/CommandResponse.php new file mode 100644 index 00000000..707b58e9 --- /dev/null +++ b/app/models/Commands/CommandResponse.php @@ -0,0 +1,49 @@ +_didFail = true; + $response->_validator = $validator; + return $response; + } + + public static function succeed($response = null) { + $cmdResponse = new CommandResponse(); + $cmdResponse->_didFail = false; + $cmdResponse->_response = $response; + return $cmdResponse; + } + + private $_validator; + private $_response; + private $_didFail; + + private function __construct() { + } + + /** + * @return bool + */ + public function didFail() { + return $this->_didFail; + } + + /** + * @return mixed + */ + public function getResponse() { + return $this->_response; + } + + /** + * @return Validator + */ + public function getValidator() { + return $this->_validator; + } + } \ No newline at end of file diff --git a/app/models/Commands/DeleteTrackCommand.php b/app/models/Commands/DeleteTrackCommand.php new file mode 100644 index 00000000..7df586d5 --- /dev/null +++ b/app/models/Commands/DeleteTrackCommand.php @@ -0,0 +1,32 @@ +_trackId = $trackId; + $this->_track = Track::find($trackId); + } + + /** + * @return bool + */ + public function authorize() { + $user = \Auth::user(); + return $this->_track && $user != null && $this->_track->user_id == $user->id; + } + + /** + * @throws \Exception + * @return CommandResponse + */ + public function execute() { + $this->_track->delete(); + return CommandResponse::succeed(); + } + } \ No newline at end of file diff --git a/app/models/Commands/EditTrackCommand.php b/app/models/Commands/EditTrackCommand.php new file mode 100644 index 00000000..c372333c --- /dev/null +++ b/app/models/Commands/EditTrackCommand.php @@ -0,0 +1,67 @@ +_trackId = $trackId; + $this->_track = Track::find($trackId); + $this->_input = $input; + } + + /** + * @return bool + */ + public function authorize() { + $user = \Auth::user(); + return $this->_track && $user != null && $this->_track->user_id == $user->id; + } + + /** + * @throws \Exception + * @return CommandResponse + */ + public function execute() { + $isVocal = isset($this->_input['is_vocal']) && $this->_input['is_vocal'] == 'true' ? true : false; + + $validator = \Validator::make($this->_input, [ + 'title' => 'required|min:3|max:80', + 'released_at' => 'before:today' . ($this->_input['released_at'] != "" ? '|date' : ''), + 'lyrics' => $isVocal ? 'required' : '', + 'license_id' => 'required|exists:licenses,id', + 'genre_id' => 'required|exists:genres,id', + 'cover' => 'image|mimes:png|min_width:350|min_height:350', + 'track_type_id' => 'required|exists:track_types,id', + 'songs' => 'required_when:track_type,2|exists:songs,id' + ]); + + if ($validator->fails()) + return CommandResponse::fail($validator); + + $track = $this->_track; + $track->title = $this->_input['title']; + $track->released_at = $this->_input['released_at'] != "" ? strtotime($this->_input['released_at']) : null; + $track->description = $this->_input['description']; + $track->lyrics = $this->_input['lyrics']; + $track->license_id = $this->_input['license_id']; + $track->genre_id = $this->_input['genre_id']; + $track->track_type_id = $this->_input['track_type_id']; + $track->is_explicit = $this->_input['is_explicit'] == 'true'; + $track->is_downloadable = $this->_input['is_downloadable'] == 'true'; + $track->is_vocal = $isVocal; + + if ($track->published_at == null) { + $track->published_at = new \DateTime(); + } + + $track->save(); + + return CommandResponse::succeed(); + } + } \ No newline at end of file diff --git a/app/models/Commands/UploadTrackCommand.php b/app/models/Commands/UploadTrackCommand.php new file mode 100644 index 00000000..f69c1767 --- /dev/null +++ b/app/models/Commands/UploadTrackCommand.php @@ -0,0 +1,84 @@ +getPathname()); + + $validator = \Validator::make(['track' => $trackFile], [ + 'track' => + 'required|' + . 'audio_format:mp3,flac,pcm_s16le ([1][0][0][0] / 0x0001),pcm_s16be,adpcm_ms ([2][0][0][0] / 0x0002),pcm_s24le ([1][0][0][0] / 0x0001),pcm_s24be,pcm_f32le ([3][0][0][0] / 0x0003),pcm_f32be (fl32 / 0x32336C66)|' + . 'audio_channels:1,2|' + . 'sample_rate:44100,48000,88200,96000,176400,192000|' + . 'min_duration:30' + ]); + + if ($validator->fails()) + return CommandResponse::fail($validator); + + $track = new Track(); + + try { + $track->user_id = $user->id; + $track->title = pathinfo($trackFile->getClientOriginalName(), PATHINFO_FILENAME); + $track->slug = \Str::slug($track->title); + $track->duration = $audio->getDuration(); + + $track->save(); + + $destination = $track->getDirectory(); + + if (!is_dir($destination)) + mkdir($destination, 755); + + $source = $trackFile->getPathname(); + $index = 0; + + $procs = []; + + foreach (Track::$Formats as $name => $format) { + $target = $destination . '/' . $track->getFilenameFor($name); + + $command = $format['command']; + $command = str_replace('{$source}', '"' . $source . '"', $command); + $command = str_replace('{$target}', '"' . $target . '"', $command); + + \Log::info('Encoding ' . $track->id . ' into ' . $target); + $this->notify('Encoding ' . $name, $index / count(Track::$Formats) * 100); + + $pipes = []; + $proc = proc_open($command, [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'a']], $pipes); + $procs[] = $proc; + } + + foreach ($procs as $proc) + proc_close($proc); + + } catch (\Exception $e) { + $track->delete(); + throw $e; + } + + return CommandResponse::succeed([ + 'id' => $track->id, + 'name' => $track->name + ]); + } + } \ No newline at end of file diff --git a/app/models/Entities/Genre.php b/app/models/Entities/Genre.php new file mode 100644 index 00000000..d110e6cf --- /dev/null +++ b/app/models/Entities/Genre.php @@ -0,0 +1,7 @@ + ['extension' => 'flac', 'tag_format' => 'metaflac', 'mime_type' => 'audio/flac', 'command' => 'ffmpeg 2>&1 -y -i {$source} -acodec flac -aq 8 -f flac {$target}'], + 'MP3' => ['extension' => 'mp3', 'tag_format' => 'id3v2.3', 'mime_type' => 'audio/mpeg', 'command' => 'ffmpeg 2>&1 -y -i {$source} -acodec libmp3lame -ab 320k -f mp3 {$target}'], + 'OGG Vorbis' => ['extension' => 'ogg', 'tag_format' => 'vorbiscomment', 'mime_type' => 'audio/ogg', 'command' => 'ffmpeg 2>&1 -y -i {$source} -acodec libvorbis -aq 7 -f ogg {$target}'], + 'AAC' => ['extension' => 'm4a', 'tag_format' => 'AtomicParsley', 'mime_type' => 'audio/mp4', 'command' => 'ffmpeg 2>&1 -y -i {$source} -acodec libfaac -ab 256k -f mp4 {$target}'], + 'ALAC' => ['extension' => 'alac.m4a', 'tag_format' => 'AtomicParsley', 'mime_type' => 'audio/mp4', 'command' => 'ffmpeg 2>&1 -y -i {$source} -acodec alac {$target}'], + ]; + + public static function summary() { + return self::select('id', 'title', 'user_id', 'slug', 'is_vocal', 'is_explicit', 'created_at', 'published_at', 'duration', 'is_downloadable', 'genre_id', 'track_type_id'); + } + + protected $table = 'tracks'; + + public function getDates() { + return ['created_at', 'deleted_at', 'published_at', 'released_at']; + } + + public function user() { + return $this->belongsTo('User'); + } + + public function getDirectory() { + $dir = (string) ( floor( $this->id / 100 ) * 100 ); + return \Config::get('app.files_directory') . '/tracks/' . $dir; + } + + public function getFilenameFor($format) { + if (!isset(self::$Formats[$format])) + throw new Exception("$format is not a valid format!"); + + $format = self::$Formats[$format]; + return "{$this->id}.{$format['extension']}"; + } + } \ No newline at end of file diff --git a/app/models/Entities/TrackType.php b/app/models/Entities/TrackType.php new file mode 100644 index 00000000..336c0e5a --- /dev/null +++ b/app/models/Entities/TrackType.php @@ -0,0 +1,7 @@ +getKey(); + } + + public function getAuthPassword() { + return $this->password_hash; + } + + public function getReminderEmail() { + return $this->email; + } + } \ No newline at end of file diff --git a/app/routes.php b/app/routes.php new file mode 100644 index 00000000..68da1bd7 --- /dev/null +++ b/app/routes.php @@ -0,0 +1,63 @@ + 'api/web'], function() { + Route::get('/taxonomies/all', 'Api\Web\TaxonomiesController@getAll'); + + Route::group(['before' => 'auth|csrf'], function() { + Route::post('/tracks/upload', 'Api\Web\TracksController@postUpload'); + Route::post('/tracks/delete/{id}', 'Api\Web\TracksController@postDelete'); + Route::post('/tracks/edit/{id}', 'Api\Web\TracksController@putEdit'); + }); + + Route::group(['before' => 'auth'], function() { + Route::get('/tracks/owned', 'Api\Web\TracksController@getOwned'); + Route::get('/tracks/edit/{id}', 'Api\Web\TracksController@getEdit'); + }); + + Route::group(['before' => 'csrf'], function(){ + Route::post('/auth/login', 'Api\Web\AuthController@postLogin'); + Route::post('/auth/logout', 'Api\Web\AuthController@postLogout'); + }); + }); + + Route::group(['prefix' => 'account'], function() { + Route::group(['before' => 'auth'], function(){ + Route::get('/favorites', 'FavoritesController@getTracks'); + Route::get('/favorites/albums', 'FavoritesController@getAlbums'); + Route::get('/favorites/playlists', 'FavoritesController@getPlaylists'); + + Route::get('/content/tracks', 'ContentController@getTracks'); + Route::get('/content/tracks/{id}', 'ContentController@getTracks'); + Route::get('/content/albums', 'ContentController@getAlbums'); + Route::get('/content/playlists', 'ContentController@getPlaylists'); + + Route::get('/', 'AccountController@getIndex'); + }); + }); + + Route::get('/', 'HomeController@getIndex'); \ No newline at end of file diff --git a/app/start/artisan.php b/app/start/artisan.php new file mode 100644 index 00000000..1df850bc --- /dev/null +++ b/app/start/artisan.php @@ -0,0 +1,13 @@ +client->request('GET', '/'); + + $this->assertTrue($this->client->getResponse()->isOk()); + + $this->assertCount(1, $crawler->filter('h1:contains("Hello World!")')); + } + +} \ No newline at end of file diff --git a/app/tests/TestCase.php b/app/tests/TestCase.php new file mode 100644 index 00000000..49b80fc2 --- /dev/null +++ b/app/tests/TestCase.php @@ -0,0 +1,19 @@ +Album Listing! +

This page should be what search engines see

+@endsection \ No newline at end of file diff --git a/app/views/artists/index.blade.php b/app/views/artists/index.blade.php new file mode 100644 index 00000000..efc046b9 --- /dev/null +++ b/app/views/artists/index.blade.php @@ -0,0 +1,6 @@ +@extends('shared._app_layout') + +@section('app_content') +

Artist Listing!

+

This page should be what search engines see

+@endsection \ No newline at end of file diff --git a/app/views/auth/login.blade.php b/app/views/auth/login.blade.php new file mode 100644 index 00000000..f9b58441 --- /dev/null +++ b/app/views/auth/login.blade.php @@ -0,0 +1,6 @@ +@extends('shared._app_layout') + +@section('app_content') +

Login

+

This page should be what search engines see

+@endsection \ No newline at end of file diff --git a/app/views/auth/register.blade.php b/app/views/auth/register.blade.php new file mode 100644 index 00000000..8f70767c --- /dev/null +++ b/app/views/auth/register.blade.php @@ -0,0 +1,6 @@ +@extends('shared._app_layout') + +@section('app_content') +

Register

+

This page should be what search engines see

+@endsection \ No newline at end of file diff --git a/app/views/emails/auth/reminder.blade.php b/app/views/emails/auth/reminder.blade.php new file mode 100644 index 00000000..2976327b --- /dev/null +++ b/app/views/emails/auth/reminder.blade.php @@ -0,0 +1,13 @@ + + + + + + +

Password Reset

+ +
+ To reset your password, complete this form: {{ URL::to('password/reset', array($token)) }}. +
+ + \ No newline at end of file diff --git a/app/views/home/index.blade.php b/app/views/home/index.blade.php new file mode 100644 index 00000000..b8290e9c --- /dev/null +++ b/app/views/home/index.blade.php @@ -0,0 +1,6 @@ +@extends('shared._app_layout') + +@section('app_content') +

Look, it's a website!

+

This page should be what search engines see

+@endsection \ No newline at end of file diff --git a/app/views/pages/about.blade.php b/app/views/pages/about.blade.php new file mode 100644 index 00000000..eb7d6bcb --- /dev/null +++ b/app/views/pages/about.blade.php @@ -0,0 +1,5 @@ +@extends('shared._app_layout') + +@section('app_content') + {{Helpers::template('pages/about.html')}} +@endsection \ No newline at end of file diff --git a/app/views/pages/faq.blade.php b/app/views/pages/faq.blade.php new file mode 100644 index 00000000..00ebfbfa --- /dev/null +++ b/app/views/pages/faq.blade.php @@ -0,0 +1,5 @@ +@extends('shared._app_layout') + +@section('app_content') + {{Helpers::template('pages/faq.html')}} +@endsection \ No newline at end of file diff --git a/app/views/playlists/index.blade.php b/app/views/playlists/index.blade.php new file mode 100644 index 00000000..4626fe77 --- /dev/null +++ b/app/views/playlists/index.blade.php @@ -0,0 +1,6 @@ +@extends('shared._app_layout') + +@section('app_content') +

Playlist Listing!

+

This page should be what search engines see

+@endsection \ No newline at end of file diff --git a/app/views/shared/_app_layout.blade.php b/app/views/shared/_app_layout.blade.php new file mode 100644 index 00000000..92c74b89 --- /dev/null +++ b/app/views/shared/_app_layout.blade.php @@ -0,0 +1,96 @@ +@extends('shared._layout') + +@section('content') +
+
+

Pony.fm

+
+
+
+
+
+
+
    +
  • +
  • +
  • +
  • +
+ +
+
+
+
+ +
+ +
+ @yield('app_content') +
+
+ + + +@endsection + +@section('styles') + {{ Assets::styleIncludes() }} +@endsection + +@section('scripts') + + + + {{ Assets::scriptIncludes() }} +@endsection \ No newline at end of file diff --git a/app/views/shared/_layout.blade.php b/app/views/shared/_layout.blade.php new file mode 100644 index 00000000..e8a15f71 --- /dev/null +++ b/app/views/shared/_layout.blade.php @@ -0,0 +1,18 @@ + + + + + + Pony.fm SPA + + + + + @yield('styles') + + + @yield('content') + + @yield('scripts') + + \ No newline at end of file diff --git a/app/views/shared/null.blade.php b/app/views/shared/null.blade.php new file mode 100644 index 00000000..771f9b9d --- /dev/null +++ b/app/views/shared/null.blade.php @@ -0,0 +1,5 @@ +@extends('shared._app_layout') + +@section('app_content') +

If you see this page, something went wrong.

+@endsection \ No newline at end of file diff --git a/app/views/tracks/index.blade.php b/app/views/tracks/index.blade.php new file mode 100644 index 00000000..007bb625 --- /dev/null +++ b/app/views/tracks/index.blade.php @@ -0,0 +1,6 @@ +@extends('shared._app_layout') + +@section('app_content') +

Track Listing!

+

This page should be what search engines see

+@endsection \ No newline at end of file diff --git a/artisan b/artisan new file mode 100644 index 00000000..1c169f6d --- /dev/null +++ b/artisan @@ -0,0 +1,74 @@ +#!/usr/bin/env php +boot(); + +/* +|-------------------------------------------------------------------------- +| Load The Artisan Console Application +|-------------------------------------------------------------------------- +| +| We'll need to run the script to load and return the Artisan console +| application. We keep this in its own script so that we will load +| the console application independent of running commands which +| will allow us to fire commands from Routes when we want to. +| +*/ + +$artisan = Illuminate\Console\Application::start($app); + +/* +|-------------------------------------------------------------------------- +| Run The Artisan Application +|-------------------------------------------------------------------------- +| +| When we run the console application, the current CLI command will be +| executed in this console and the response sent back to a terminal +| or another output device for the developers. Here goes nothing! +| +*/ + +$status = $artisan->run(); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running. We will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$app->shutdown(); + +exit($status); \ No newline at end of file diff --git a/bootstrap/autoload.php b/bootstrap/autoload.php new file mode 100644 index 00000000..6b329312 --- /dev/null +++ b/bootstrap/autoload.php @@ -0,0 +1,75 @@ +instances[$abstract]); + } + public function bind($abstract, $concrete = null, $shared = false) + { + if (is_array($abstract)) { + list($abstract, $alias) = $this->extractAlias($abstract); + $this->alias($abstract, $alias); + } + unset($this->instances[$abstract]); + if (is_null($concrete)) { + $concrete = $abstract; + } + if (!$concrete instanceof Closure) { + $concrete = function ($c) use($abstract, $concrete) { + $method = $abstract == $concrete ? 'build' : 'make'; + return $c->{$method}($concrete); + }; + } + $this->bindings[$abstract] = compact('concrete', 'shared'); + } + public function bindIf($abstract, $concrete = null, $shared = false) + { + if (!$this->bound($abstract)) { + $this->bind($abstract, $concrete, $shared); + } + } + public function singleton($abstract, $concrete = null) + { + return $this->bind($abstract, $concrete, true); + } + public function share(Closure $closure) + { + return function ($container) use($closure) { + static $object; + if (is_null($object)) { + $object = $closure($container); + } + return $object; + }; + } + public function extend($abstract, Closure $closure) + { + if (!isset($this->bindings[$abstract])) { + throw new \InvalidArgumentException("Type {$abstract} is not bound."); + } + $resolver = $this->bindings[$abstract]['concrete']; + $this->bind($abstract, function ($container) use($resolver, $closure) { + return $closure($resolver($container), $container); + }, $this->isShared($abstract)); + } + public function instance($abstract, $instance) + { + if (is_array($abstract)) { + list($abstract, $alias) = $this->extractAlias($abstract); + $this->alias($abstract, $alias); + } + $this->instances[$abstract] = $instance; + } + public function alias($abstract, $alias) + { + $this->aliases[$alias] = $abstract; + } + protected function extractAlias(array $definition) + { + return array(key($definition), current($definition)); + } + public function make($abstract, $parameters = array()) + { + $abstract = $this->getAlias($abstract); + if (isset($this->instances[$abstract])) { + return $this->instances[$abstract]; + } + $concrete = $this->getConcrete($abstract); + if ($this->isBuildable($concrete, $abstract)) { + $object = $this->build($concrete, $parameters); + } else { + $object = $this->make($concrete, $parameters); + } + if ($this->isShared($abstract)) { + $this->instances[$abstract] = $object; + } + $this->fireResolvingCallbacks($object); + return $object; + } + protected function getConcrete($abstract) + { + if (!isset($this->bindings[$abstract])) { + return $abstract; + } else { + return $this->bindings[$abstract]['concrete']; + } + } + public function build($concrete, $parameters = array()) + { + if ($concrete instanceof Closure) { + return $concrete($this, $parameters); + } + $reflector = new \ReflectionClass($concrete); + if (!$reflector->isInstantiable()) { + $message = "Target [{$concrete}] is not instantiable."; + throw new BindingResolutionException($message); + } + $constructor = $reflector->getConstructor(); + if (is_null($constructor)) { + return new $concrete(); + } + $parameters = $constructor->getParameters(); + $dependencies = $this->getDependencies($parameters); + return $reflector->newInstanceArgs($dependencies); + } + protected function getDependencies($parameters) + { + $dependencies = array(); + foreach ($parameters as $parameter) { + $dependency = $parameter->getClass(); + if (is_null($dependency)) { + $dependencies[] = $this->resolveNonClass($parameter); + } else { + $dependencies[] = $this->make($dependency->name); + } + } + return (array) $dependencies; + } + protected function resolveNonClass(ReflectionParameter $parameter) + { + if ($parameter->isDefaultValueAvailable()) { + return $parameter->getDefaultValue(); + } else { + $message = "Unresolvable dependency resolving [{$parameter}]."; + throw new BindingResolutionException($message); + } + } + public function resolving(Closure $callback) + { + $this->resolvingCallbacks[] = $callback; + } + protected function fireResolvingCallbacks($object) + { + foreach ($this->resolvingCallbacks as $callback) { + call_user_func($callback, $object); + } + } + protected function isShared($abstract) + { + $set = isset($this->bindings[$abstract]['shared']); + return $set and $this->bindings[$abstract]['shared'] === true; + } + protected function isBuildable($concrete, $abstract) + { + return $concrete === $abstract or $concrete instanceof Closure; + } + protected function getAlias($abstract) + { + return isset($this->aliases[$abstract]) ? $this->aliases[$abstract] : $abstract; + } + public function getBindings() + { + return $this->bindings; + } + public function offsetExists($key) + { + return isset($this->bindings[$key]); + } + public function offsetGet($key) + { + return $this->make($key); + } + public function offsetSet($key, $value) + { + if (!$value instanceof Closure) { + $value = function () use($value) { + return $value; + }; + } + $this->bind($key, $value); + } + public function offsetUnset($key) + { + unset($this->bindings[$key]); + } +} +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +interface HttpKernelInterface +{ + const MASTER_REQUEST = 1; + const SUB_REQUEST = 2; + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); +} +namespace Illuminate\Support\Contracts; + +interface ResponsePreparerInterface +{ + public function prepareResponse($value); +} +namespace Illuminate\Foundation; + +use Closure; +use Illuminate\Http\Request; +use Illuminate\Http\Response; +use Illuminate\Routing\Route; +use Illuminate\Routing\Router; +use Illuminate\Config\FileLoader; +use Illuminate\Container\Container; +use Illuminate\Filesystem\Filesystem; +use Illuminate\Support\Facades\Facade; +use Illuminate\Support\ServiceProvider; +use Illuminate\Events\EventServiceProvider; +use Illuminate\Foundation\ProviderRepository; +use Illuminate\Routing\RoutingServiceProvider; +use Illuminate\Exception\ExceptionServiceProvider; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Illuminate\Support\Contracts\ResponsePreparerInterface; +use Symfony\Component\HttpKernel\Exception\FatalErrorException; +use Symfony\Component\HttpFoundation\Request as SymfonyRequest; +use Symfony\Component\HttpFoundation\Response as SymfonyResponse; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpFoundation\RedirectResponse as SymfonyRedirect; +class Application extends Container implements HttpKernelInterface, ResponsePreparerInterface +{ + const VERSION = '4.0.2'; + protected $booted = false; + protected $bootingCallbacks = array(); + protected $bootedCallbacks = array(); + protected $shutdownCallbacks = array(); + protected $serviceProviders = array(); + protected $loadedProviders = array(); + protected $deferredServices = array(); + public function __construct(Request $request = null) + { + $this['request'] = $this->createRequest($request); + $this->register(new ExceptionServiceProvider($this)); + $this->register(new RoutingServiceProvider($this)); + $this->register(new EventServiceProvider($this)); + } + protected function createRequest(Request $request = null) + { + return $request ?: Request::createFromGlobals(); + } + public function setRequestForConsoleEnvironment() + { + $url = $this['config']->get('app.url', 'http://localhost'); + $this->instance('request', Request::create($url, 'GET', array(), array(), array(), $_SERVER)); + } + public function redirectIfTrailingSlash() + { + if ($this->runningInConsole()) { + return; + } + $path = $this['request']->getPathInfo(); + if ($path != '/' and ends_with($path, '/') and !ends_with($path, '//')) { + with(new SymfonyRedirect($this['request']->fullUrl(), 301))->send(); + die; + } + } + public function bindInstallPaths(array $paths) + { + $this->instance('path', realpath($paths['app'])); + foreach (array_except($paths, array('app')) as $key => $value) { + $this->instance("path.{$key}", realpath($value)); + } + } + public static function getBootstrapFile() + { + return 'F:\\Nelson\\My Documents - Personal\\Visual Studio 2010\\Projects\\Poniverse\\spa.pony.fm\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation' . '/start.php'; + } + public function startExceptionHandling() + { + $this['exception']->register($this->environment()); + $this['exception']->setDebug($this['config']['app.debug']); + } + public function environment() + { + return $this['env']; + } + public function detectEnvironment($environments) + { + $base = $this['request']->getHost(); + $arguments = $this['request']->server->get('argv'); + if ($this->runningInConsole()) { + return $this->detectConsoleEnvironment($base, $environments, $arguments); + } + return $this->detectWebEnvironment($base, $environments); + } + protected function detectWebEnvironment($base, $environments) + { + if ($environments instanceof Closure) { + return $this['env'] = call_user_func($environments); + } + foreach ($environments as $environment => $hosts) { + foreach ((array) $hosts as $host) { + if (str_is($host, $base) or $this->isMachine($host)) { + return $this['env'] = $environment; + } + } + } + return $this['env'] = 'production'; + } + protected function detectConsoleEnvironment($base, $environments, $arguments) + { + foreach ($arguments as $key => $value) { + if (starts_with($value, '--env=')) { + $segments = array_slice(explode('=', $value), 1); + return $this['env'] = head($segments); + } + } + return $this->detectWebEnvironment($base, $environments); + } + protected function isMachine($name) + { + return str_is($name, gethostname()); + } + public function runningInConsole() + { + return php_sapi_name() == 'cli'; + } + public function runningUnitTests() + { + return $this['env'] == 'testing'; + } + public function register($provider, $options = array()) + { + if (is_string($provider)) { + $provider = $this->resolveProviderClass($provider); + } + $provider->register(); + foreach ($options as $key => $value) { + $this[$key] = $value; + } + $this->serviceProviders[] = $provider; + $this->loadedProviders[get_class($provider)] = true; + } + protected function resolveProviderClass($provider) + { + return new $provider($this); + } + public function loadDeferredProviders() + { + foreach (array_unique($this->deferredServices) as $provider) { + $this->register($instance = new $provider($this)); + if ($this->booted) { + $instance->boot(); + } + } + $this->deferredServices = array(); + } + protected function loadDeferredProvider($service) + { + $provider = $this->deferredServices[$service]; + if (!isset($this->loadedProviders[$provider])) { + $this->register($instance = new $provider($this)); + unset($this->deferredServices[$service]); + $this->setupDeferredBoot($instance); + } + } + protected function setupDeferredBoot($instance) + { + if ($this->booted) { + return $instance->boot(); + } + $this->booting(function () use($instance) { + $instance->boot(); + }); + } + public function make($abstract, $parameters = array()) + { + if (isset($this->deferredServices[$abstract])) { + $this->loadDeferredProvider($abstract); + } + return parent::make($abstract, $parameters); + } + public function before($callback) + { + return $this['router']->before($callback); + } + public function after($callback) + { + return $this['router']->after($callback); + } + public function close($callback) + { + return $this['router']->close($callback); + } + public function finish($callback) + { + $this['router']->finish($callback); + } + public function shutdown($callback = null) + { + if (is_null($callback)) { + $this->fireAppCallbacks($this->shutdownCallbacks); + } else { + $this->shutdownCallbacks[] = $callback; + } + } + public function run() + { + $response = $this->dispatch($this['request']); + $this['router']->callCloseFilter($this['request'], $response); + $response->send(); + $this['router']->callFinishFilter($this['request'], $response); + } + public function dispatch(Request $request) + { + if ($this->isDownForMaintenance()) { + $response = $this['events']->until('illuminate.app.down'); + return $this->prepareResponse($response, $request); + } else { + return $this['router']->dispatch($this->prepareRequest($request)); + } + } + public function handle(SymfonyRequest $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + $this->instance('request', $request); + Facade::clearResolvedInstance('request'); + return $this->dispatch($request); + } + public function boot() + { + if ($this->booted) { + return; + } + foreach ($this->serviceProviders as $provider) { + $provider->boot(); + } + $this->fireAppCallbacks($this->bootingCallbacks); + $this->booted = true; + $this->fireAppCallbacks($this->bootedCallbacks); + } + public function booting($callback) + { + $this->bootingCallbacks[] = $callback; + } + public function booted($callback) + { + $this->bootedCallbacks[] = $callback; + } + protected function fireAppCallbacks(array $callbacks) + { + foreach ($callbacks as $callback) { + call_user_func($callback, $this); + } + } + public function prepareRequest(Request $request) + { + if (isset($this['session'])) { + $request->setSessionStore($this['session']); + } + return $request; + } + public function prepareResponse($value) + { + if (!$value instanceof SymfonyResponse) { + $value = new Response($value); + } + return $value->prepare($this['request']); + } + public function isDownForMaintenance() + { + return file_exists($this['path.storage'] . '/meta/down'); + } + public function down(Closure $callback) + { + $this['events']->listen('illuminate.app.down', $callback); + } + public function abort($code, $message = '', array $headers = array()) + { + if ($code == 404) { + throw new NotFoundHttpException($message); + } else { + throw new HttpException($code, $message, null, $headers); + } + } + public function missing(Closure $callback) + { + $this->error(function (NotFoundHttpException $e) use($callback) { + return call_user_func($callback, $e); + }); + } + public function error(Closure $callback) + { + $this['exception']->error($callback); + } + public function pushError(Closure $closure) + { + $this['exception']->pushError($callback); + } + public function fatal(Closure $callback) + { + $this->error(function (FatalErrorException $e) use($callback) { + return call_user_func($callback, $e); + }); + } + public function getConfigLoader() + { + return new FileLoader(new Filesystem(), $this['path'] . '/config'); + } + public function getProviderRepository() + { + $manifest = $this['config']['app.manifest']; + return new ProviderRepository(new Filesystem(), $manifest); + } + public function setLocale($locale) + { + $this['config']->set('app.locale', $locale); + $this['translator']->setLocale($locale); + $this['events']->fire('locale.changed', array($locale)); + } + public function getLoadedProviders() + { + return $this->loadedProviders; + } + public function setDeferredServices(array $services) + { + $this->deferredServices = $services; + } + public function __get($key) + { + return $this[$key]; + } + public function __set($key, $value) + { + $this[$key] = $value; + } +} +namespace Illuminate\Http; + +use Illuminate\Session\Store as SessionStore; +use Symfony\Component\HttpFoundation\ParameterBag; +class Request extends \Symfony\Component\HttpFoundation\Request +{ + protected $json; + protected $sessionStore; + public function instance() + { + return $this; + } + public function root() + { + return rtrim($this->getSchemeAndHttpHost() . $this->getBaseUrl(), '/'); + } + public function url() + { + return rtrim(preg_replace('/\\?.*/', '', $this->getUri()), '/'); + } + public function fullUrl() + { + $query = $this->getQueryString(); + return $query ? $this->url() . '?' . $query : $this->url(); + } + public function path() + { + $pattern = trim($this->getPathInfo(), '/'); + return $pattern == '' ? '/' : $pattern; + } + public function segment($index, $default = null) + { + $segments = explode('/', trim($this->getPathInfo(), '/')); + $segments = array_filter($segments, function ($v) { + return $v != ''; + }); + return array_get($segments, $index - 1, $default); + } + public function segments() + { + $path = $this->path(); + return $path == '/' ? array() : explode('/', $path); + } + public function is($pattern) + { + foreach (func_get_args() as $pattern) { + if (str_is($pattern, $this->path())) { + return true; + } + } + return false; + } + public function ajax() + { + return $this->isXmlHttpRequest(); + } + public function secure() + { + return $this->isSecure(); + } + public function has($key) + { + if (count(func_get_args()) > 1) { + foreach (func_get_args() as $value) { + if (!$this->has($value)) { + return false; + } + } + return true; + } + if (is_array($this->input($key))) { + return true; + } + return trim((string) $this->input($key)) !== ''; + } + public function all() + { + return $this->input() + $this->files->all(); + } + public function input($key = null, $default = null) + { + $input = $this->getInputSource()->all() + $this->query->all(); + return array_get($input, $key, $default); + } + public function only($keys) + { + $keys = is_array($keys) ? $keys : func_get_args(); + return array_only($this->input(), $keys) + array_fill_keys($keys, null); + } + public function except($keys) + { + $keys = is_array($keys) ? $keys : func_get_args(); + $results = $this->input(); + foreach ($keys as $key) { + array_forget($results, $key); + } + return $results; + } + public function query($key = null, $default = null) + { + return $this->retrieveItem('query', $key, $default); + } + public function cookie($key = null, $default = null) + { + return $this->retrieveItem('cookies', $key, $default); + } + public function file($key = null, $default = null) + { + return $this->retrieveItem('files', $key, $default); + } + public function hasFile($key) + { + return $this->files->has($key) and !is_null($this->file($key)); + } + public function header($key = null, $default = null) + { + return $this->retrieveItem('headers', $key, $default); + } + public function server($key = null, $default = null) + { + return $this->retrieveItem('server', $key, $default); + } + public function old($key = null, $default = null) + { + return $this->getSessionStore()->getOldInput($key, $default); + } + public function flash($filter = null, $keys = array()) + { + $flash = !is_null($filter) ? $this->{$filter}($keys) : $this->input(); + $this->getSessionStore()->flashInput($flash); + } + public function flashOnly($keys) + { + $keys = is_array($keys) ? $keys : func_get_args(); + return $this->flash('only', $keys); + } + public function flashExcept($keys) + { + $keys = is_array($keys) ? $keys : func_get_args(); + return $this->flash('except', $keys); + } + public function flush() + { + $this->getSessionStore()->flashInput(array()); + } + protected function retrieveItem($source, $key, $default) + { + if (is_null($key)) { + return $this->{$source}->all(); + } else { + return $this->{$source}->get($key, $default, true); + } + } + public function merge(array $input) + { + $this->getInputSource()->add($input); + } + public function replace(array $input) + { + $this->getInputSource()->replace($input); + } + public function json($key = null, $default = null) + { + if (!isset($this->json)) { + $this->json = new ParameterBag((array) json_decode($this->getContent(), true)); + } + if (is_null($key)) { + return $this->json; + } + return array_get($this->json->all(), $key, $default); + } + protected function getInputSource() + { + if ($this->isJson()) { + return $this->json(); + } + return $this->getMethod() == 'GET' ? $this->query : $this->request; + } + public function isJson() + { + return str_contains($this->header('CONTENT_TYPE'), '/json'); + } + public function wantsJson() + { + $acceptable = $this->getAcceptableContentTypes(); + return isset($acceptable[0]) and $acceptable[0] == 'application/json'; + } + public function getSessionStore() + { + if (!isset($this->sessionStore)) { + throw new \RuntimeException('Session store not set on request.'); + } + return $this->sessionStore; + } + public function setSessionStore(SessionStore $session) + { + $this->sessionStore = $session; + } + public function hasSessionStore() + { + return isset($this->sessionStore); + } +} +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\Session\SessionInterface; +class Request +{ + const HEADER_CLIENT_IP = 'client_ip'; + const HEADER_CLIENT_HOST = 'client_host'; + const HEADER_CLIENT_PROTO = 'client_proto'; + const HEADER_CLIENT_PORT = 'client_port'; + protected static $trustedProxies = array(); + protected static $trustedHeaders = array(self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT'); + protected static $httpMethodParameterOverride = false; + public $attributes; + public $request; + public $query; + public $server; + public $files; + public $cookies; + public $headers; + protected $content; + protected $languages; + protected $charsets; + protected $acceptableContentTypes; + protected $pathInfo; + protected $requestUri; + protected $baseUrl; + protected $basePath; + protected $method; + protected $format; + protected $session; + protected $locale; + protected $defaultLocale = 'en'; + protected static $formats; + public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); + } + public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->request = new ParameterBag($request); + $this->query = new ParameterBag($query); + $this->attributes = new ParameterBag($attributes); + $this->cookies = new ParameterBag($cookies); + $this->files = new FileBag($files); + $this->server = new ServerBag($server); + $this->headers = new HeaderBag($this->server->getHeaders()); + $this->content = $content; + $this->languages = null; + $this->charsets = null; + $this->acceptableContentTypes = null; + $this->pathInfo = null; + $this->requestUri = null; + $this->baseUrl = null; + $this->basePath = null; + $this->method = null; + $this->format = null; + } + public static function createFromGlobals() + { + $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); + if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))) { + parse_str($request->getContent(), $data); + $request->request = new ParameterBag($data); + } + return $request; + } + public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) + { + $server = array_replace(array('SERVER_NAME' => 'localhost', 'SERVER_PORT' => 80, 'HTTP_HOST' => 'localhost', 'HTTP_USER_AGENT' => 'Symfony/2.X', 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'REMOTE_ADDR' => '127.0.0.1', 'SCRIPT_NAME' => '', 'SCRIPT_FILENAME' => '', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'REQUEST_TIME' => time()), $server); + $server['PATH_INFO'] = ''; + $server['REQUEST_METHOD'] = strtoupper($method); + $components = parse_url($uri); + if (isset($components['host'])) { + $server['SERVER_NAME'] = $components['host']; + $server['HTTP_HOST'] = $components['host']; + } + if (isset($components['scheme'])) { + if ('https' === $components['scheme']) { + $server['HTTPS'] = 'on'; + $server['SERVER_PORT'] = 443; + } else { + unset($server['HTTPS']); + $server['SERVER_PORT'] = 80; + } + } + if (isset($components['port'])) { + $server['SERVER_PORT'] = $components['port']; + $server['HTTP_HOST'] = $server['HTTP_HOST'] . ':' . $components['port']; + } + if (isset($components['user'])) { + $server['PHP_AUTH_USER'] = $components['user']; + } + if (isset($components['pass'])) { + $server['PHP_AUTH_PW'] = $components['pass']; + } + if (!isset($components['path'])) { + $components['path'] = '/'; + } + switch (strtoupper($method)) { + case 'POST': + case 'PUT': + case 'DELETE': + if (!isset($server['CONTENT_TYPE'])) { + $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + } + case 'PATCH': + $request = $parameters; + $query = array(); + break; + default: + $request = array(); + $query = $parameters; + break; + } + if (isset($components['query'])) { + parse_str(html_entity_decode($components['query']), $qs); + $query = array_replace($qs, $query); + } + $queryString = http_build_query($query, '', '&'); + $server['REQUEST_URI'] = $components['path'] . ('' !== $queryString ? '?' . $queryString : ''); + $server['QUERY_STRING'] = $queryString; + return new static($query, $request, array(), $cookies, $files, $server, $content); + } + public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + { + $dup = clone $this; + if ($query !== null) { + $dup->query = new ParameterBag($query); + } + if ($request !== null) { + $dup->request = new ParameterBag($request); + } + if ($attributes !== null) { + $dup->attributes = new ParameterBag($attributes); + } + if ($cookies !== null) { + $dup->cookies = new ParameterBag($cookies); + } + if ($files !== null) { + $dup->files = new FileBag($files); + } + if ($server !== null) { + $dup->server = new ServerBag($server); + $dup->headers = new HeaderBag($dup->server->getHeaders()); + } + $dup->languages = null; + $dup->charsets = null; + $dup->acceptableContentTypes = null; + $dup->pathInfo = null; + $dup->requestUri = null; + $dup->baseUrl = null; + $dup->basePath = null; + $dup->method = null; + $dup->format = null; + return $dup; + } + public function __clone() + { + $this->query = clone $this->query; + $this->request = clone $this->request; + $this->attributes = clone $this->attributes; + $this->cookies = clone $this->cookies; + $this->files = clone $this->files; + $this->server = clone $this->server; + $this->headers = clone $this->headers; + } + public function __toString() + { + return sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL')) . ' +' . $this->headers . ' +' . $this->getContent(); + } + public function overrideGlobals() + { + $_GET = $this->query->all(); + $_POST = $this->request->all(); + $_SERVER = $this->server->all(); + $_COOKIE = $this->cookies->all(); + foreach ($this->headers->all() as $key => $value) { + $key = strtoupper(str_replace('-', '_', $key)); + if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { + $_SERVER[$key] = implode(', ', $value); + } else { + $_SERVER['HTTP_' . $key] = implode(', ', $value); + } + } + $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE); + $requestOrder = ini_get('request_order') ?: ini_get('variable_order'); + $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp'; + $_REQUEST = array(); + foreach (str_split($requestOrder) as $order) { + $_REQUEST = array_merge($_REQUEST, $request[$order]); + } + } + public static function setTrustedProxies(array $proxies) + { + self::$trustedProxies = $proxies; + } + public static function getTrustedProxies() + { + return self::$trustedProxies; + } + public static function setTrustedHeaderName($key, $value) + { + if (!array_key_exists($key, self::$trustedHeaders)) { + throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); + } + self::$trustedHeaders[$key] = $value; + } + public static function getTrustedHeaderName($key) + { + if (!array_key_exists($key, self::$trustedHeaders)) { + throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key)); + } + return self::$trustedHeaders[$key]; + } + public static function normalizeQueryString($qs) + { + if ('' == $qs) { + return ''; + } + $parts = array(); + $order = array(); + foreach (explode('&', $qs) as $param) { + if ('' === $param || '=' === $param[0]) { + continue; + } + $keyValuePair = explode('=', $param, 2); + $parts[] = isset($keyValuePair[1]) ? rawurlencode(urldecode($keyValuePair[0])) . '=' . rawurlencode(urldecode($keyValuePair[1])) : rawurlencode(urldecode($keyValuePair[0])); + $order[] = urldecode($keyValuePair[0]); + } + array_multisort($order, SORT_ASC, $parts); + return implode('&', $parts); + } + public static function enableHttpMethodParameterOverride() + { + self::$httpMethodParameterOverride = true; + } + public static function getHttpMethodParameterOverride() + { + return self::$httpMethodParameterOverride; + } + public function get($key, $default = null, $deep = false) + { + return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep); + } + public function getSession() + { + return $this->session; + } + public function hasPreviousSession() + { + return $this->hasSession() && $this->cookies->has($this->session->getName()); + } + public function hasSession() + { + return null !== $this->session; + } + public function setSession(SessionInterface $session) + { + $this->session = $session; + } + public function getClientIps() + { + $ip = $this->server->get('REMOTE_ADDR'); + if (!self::$trustedProxies) { + return array($ip); + } + if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { + return array($ip); + } + $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); + $clientIps[] = $ip; + $trustedProxies = !self::$trustedProxies ? array($ip) : self::$trustedProxies; + $ip = $clientIps[0]; + foreach ($clientIps as $key => $clientIp) { + if (IpUtils::checkIp($clientIp, $trustedProxies)) { + unset($clientIps[$key]); + continue; + } + } + return $clientIps ? array_reverse($clientIps) : array($ip); + } + public function getClientIp() + { + $ipAddresses = $this->getClientIps(); + return $ipAddresses[0]; + } + public function getScriptName() + { + return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', '')); + } + public function getPathInfo() + { + if (null === $this->pathInfo) { + $this->pathInfo = $this->preparePathInfo(); + } + return $this->pathInfo; + } + public function getBasePath() + { + if (null === $this->basePath) { + $this->basePath = $this->prepareBasePath(); + } + return $this->basePath; + } + public function getBaseUrl() + { + if (null === $this->baseUrl) { + $this->baseUrl = $this->prepareBaseUrl(); + } + return $this->baseUrl; + } + public function getScheme() + { + return $this->isSecure() ? 'https' : 'http'; + } + public function getPort() + { + if (self::$trustedProxies) { + if (self::$trustedHeaders[self::HEADER_CLIENT_PORT] && ($port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT]))) { + return $port; + } + if (self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && 'https' === $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO], 'http')) { + return 443; + } + } + return $this->server->get('SERVER_PORT'); + } + public function getUser() + { + return $this->server->get('PHP_AUTH_USER'); + } + public function getPassword() + { + return $this->server->get('PHP_AUTH_PW'); + } + public function getUserInfo() + { + $userinfo = $this->getUser(); + $pass = $this->getPassword(); + if ('' != $pass) { + $userinfo .= ":{$pass}"; + } + return $userinfo; + } + public function getHttpHost() + { + $scheme = $this->getScheme(); + $port = $this->getPort(); + if ('http' == $scheme && $port == 80 || 'https' == $scheme && $port == 443) { + return $this->getHost(); + } + return $this->getHost() . ':' . $port; + } + public function getRequestUri() + { + if (null === $this->requestUri) { + $this->requestUri = $this->prepareRequestUri(); + } + return $this->requestUri; + } + public function getSchemeAndHttpHost() + { + return $this->getScheme() . '://' . $this->getHttpHost(); + } + public function getUri() + { + if (null !== ($qs = $this->getQueryString())) { + $qs = '?' . $qs; + } + return $this->getSchemeAndHttpHost() . $this->getBaseUrl() . $this->getPathInfo() . $qs; + } + public function getUriForPath($path) + { + return $this->getSchemeAndHttpHost() . $this->getBaseUrl() . $path; + } + public function getQueryString() + { + $qs = static::normalizeQueryString($this->server->get('QUERY_STRING')); + return '' === $qs ? null : $qs; + } + public function isSecure() + { + if (self::$trustedProxies && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && ($proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO]))) { + return in_array(strtolower($proto), array('https', 'on', '1')); + } + return 'on' == strtolower($this->server->get('HTTPS')) || 1 == $this->server->get('HTTPS'); + } + public function getHost() + { + if (self::$trustedProxies && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && ($host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST]))) { + $elements = explode(',', $host); + $host = $elements[count($elements) - 1]; + } elseif (!($host = $this->headers->get('HOST'))) { + if (!($host = $this->server->get('SERVER_NAME'))) { + $host = $this->server->get('SERVER_ADDR', ''); + } + } + $host = strtolower(preg_replace('/:\\d+$/', '', trim($host))); + if ($host && !preg_match('/^\\[?(?:[a-zA-Z0-9-:\\]_]+\\.?)+$/', $host)) { + throw new \UnexpectedValueException('Invalid Host'); + } + return $host; + } + public function setMethod($method) + { + $this->method = null; + $this->server->set('REQUEST_METHOD', $method); + } + public function getMethod() + { + if (null === $this->method) { + $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + if ('POST' === $this->method) { + if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) { + $this->method = strtoupper($method); + } elseif (self::$httpMethodParameterOverride) { + $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST'))); + } + } + } + return $this->method; + } + public function getRealMethod() + { + return strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + } + public function getMimeType($format) + { + if (null === static::$formats) { + static::initializeFormats(); + } + return isset(static::$formats[$format]) ? static::$formats[$format][0] : null; + } + public function getFormat($mimeType) + { + if (false !== ($pos = strpos($mimeType, ';'))) { + $mimeType = substr($mimeType, 0, $pos); + } + if (null === static::$formats) { + static::initializeFormats(); + } + foreach (static::$formats as $format => $mimeTypes) { + if (in_array($mimeType, (array) $mimeTypes)) { + return $format; + } + } + return null; + } + public function setFormat($format, $mimeTypes) + { + if (null === static::$formats) { + static::initializeFormats(); + } + static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); + } + public function getRequestFormat($default = 'html') + { + if (null === $this->format) { + $this->format = $this->get('_format', $default); + } + return $this->format; + } + public function setRequestFormat($format) + { + $this->format = $format; + } + public function getContentType() + { + return $this->getFormat($this->headers->get('CONTENT_TYPE')); + } + public function setDefaultLocale($locale) + { + $this->defaultLocale = $locale; + if (null === $this->locale) { + $this->setPhpDefaultLocale($locale); + } + } + public function setLocale($locale) + { + $this->setPhpDefaultLocale($this->locale = $locale); + } + public function getLocale() + { + return null === $this->locale ? $this->defaultLocale : $this->locale; + } + public function isMethod($method) + { + return $this->getMethod() === strtoupper($method); + } + public function isMethodSafe() + { + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + public function getContent($asResource = false) + { + if (false === $this->content || true === $asResource && null !== $this->content) { + throw new \LogicException('getContent() can only be called once when using the resource return type.'); + } + if (true === $asResource) { + $this->content = false; + return fopen('php://input', 'rb'); + } + if (null === $this->content) { + $this->content = file_get_contents('php://input'); + } + return $this->content; + } + public function getETags() + { + return preg_split('/\\s*,\\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY); + } + public function isNoCache() + { + return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma'); + } + public function getPreferredLanguage(array $locales = null) + { + $preferredLanguages = $this->getLanguages(); + if (empty($locales)) { + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null; + } + if (!$preferredLanguages) { + return $locales[0]; + } + $extendedPreferredLanguages = array(); + foreach ($preferredLanguages as $language) { + $extendedPreferredLanguages[] = $language; + if (false !== ($position = strpos($language, '_'))) { + $superLanguage = substr($language, 0, $position); + if (!in_array($superLanguage, $preferredLanguages)) { + $extendedPreferredLanguages[] = $superLanguage; + } + } + } + $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales)); + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0]; + } + public function getLanguages() + { + if (null !== $this->languages) { + return $this->languages; + } + $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); + $this->languages = array(); + foreach (array_keys($languages) as $lang) { + if (strstr($lang, '-')) { + $codes = explode('-', $lang); + if ($codes[0] == 'i') { + if (count($codes) > 1) { + $lang = $codes[1]; + } + } else { + for ($i = 0, $max = count($codes); $i < $max; $i++) { + if ($i == 0) { + $lang = strtolower($codes[0]); + } else { + $lang .= '_' . strtoupper($codes[$i]); + } + } + } + } + $this->languages[] = $lang; + } + return $this->languages; + } + public function getCharsets() + { + if (null !== $this->charsets) { + return $this->charsets; + } + return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()); + } + public function getAcceptableContentTypes() + { + if (null !== $this->acceptableContentTypes) { + return $this->acceptableContentTypes; + } + return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()); + } + public function isXmlHttpRequest() + { + return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); + } + protected function prepareRequestUri() + { + $requestUri = ''; + if ($this->headers->has('X_ORIGINAL_URL')) { + $requestUri = $this->headers->get('X_ORIGINAL_URL'); + $this->headers->remove('X_ORIGINAL_URL'); + $this->server->remove('HTTP_X_ORIGINAL_URL'); + $this->server->remove('UNENCODED_URL'); + $this->server->remove('IIS_WasUrlRewritten'); + } elseif ($this->headers->has('X_REWRITE_URL')) { + $requestUri = $this->headers->get('X_REWRITE_URL'); + $this->headers->remove('X_REWRITE_URL'); + } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') { + $requestUri = $this->server->get('UNENCODED_URL'); + $this->server->remove('UNENCODED_URL'); + $this->server->remove('IIS_WasUrlRewritten'); + } elseif ($this->server->has('REQUEST_URI')) { + $requestUri = $this->server->get('REQUEST_URI'); + $schemeAndHttpHost = $this->getSchemeAndHttpHost(); + if (strpos($requestUri, $schemeAndHttpHost) === 0) { + $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); + } + } elseif ($this->server->has('ORIG_PATH_INFO')) { + $requestUri = $this->server->get('ORIG_PATH_INFO'); + if ('' != $this->server->get('QUERY_STRING')) { + $requestUri .= '?' . $this->server->get('QUERY_STRING'); + } + $this->server->remove('ORIG_PATH_INFO'); + } + $this->server->set('REQUEST_URI', $requestUri); + return $requestUri; + } + protected function prepareBaseUrl() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + if (basename($this->server->get('SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('SCRIPT_NAME'); + } elseif (basename($this->server->get('PHP_SELF')) === $filename) { + $baseUrl = $this->server->get('PHP_SELF'); + } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); + } else { + $path = $this->server->get('PHP_SELF', ''); + $file = $this->server->get('SCRIPT_FILENAME', ''); + $segs = explode('/', trim($file, '/')); + $segs = array_reverse($segs); + $index = 0; + $last = count($segs); + $baseUrl = ''; + do { + $seg = $segs[$index]; + $baseUrl = '/' . $seg . $baseUrl; + ++$index; + } while ($last > $index && false !== ($pos = strpos($path, $baseUrl)) && 0 != $pos); + } + $requestUri = $this->getRequestUri(); + if ($baseUrl && false !== ($prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl))) { + return $prefix; + } + if ($baseUrl && false !== ($prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl)))) { + return rtrim($prefix, '/'); + } + $truncatedRequestUri = $requestUri; + if (($pos = strpos($requestUri, '?')) !== false) { + $truncatedRequestUri = substr($requestUri, 0, $pos); + } + $basename = basename($baseUrl); + if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) { + return ''; + } + if (strlen($requestUri) >= strlen($baseUrl) && (false !== ($pos = strpos($requestUri, $baseUrl)) && $pos !== 0)) { + $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); + } + return rtrim($baseUrl, '/'); + } + protected function prepareBasePath() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + $baseUrl = $this->getBaseUrl(); + if (empty($baseUrl)) { + return ''; + } + if (basename($baseUrl) === $filename) { + $basePath = dirname($baseUrl); + } else { + $basePath = $baseUrl; + } + if ('\\' === DIRECTORY_SEPARATOR) { + $basePath = str_replace('\\', '/', $basePath); + } + return rtrim($basePath, '/'); + } + protected function preparePathInfo() + { + $baseUrl = $this->getBaseUrl(); + if (null === ($requestUri = $this->getRequestUri())) { + return '/'; + } + $pathInfo = '/'; + if ($pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + if (null !== $baseUrl && false === ($pathInfo = substr($requestUri, strlen($baseUrl)))) { + return '/'; + } elseif (null === $baseUrl) { + return $requestUri; + } + return (string) $pathInfo; + } + protected static function initializeFormats() + { + static::$formats = array('html' => array('text/html', 'application/xhtml+xml'), 'txt' => array('text/plain'), 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), 'css' => array('text/css'), 'json' => array('application/json', 'application/x-json'), 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), 'rdf' => array('application/rdf+xml'), 'atom' => array('application/atom+xml'), 'rss' => array('application/rss+xml')); + } + private function setPhpDefaultLocale($locale) + { + try { + if (class_exists('Locale', false)) { + \Locale::setDefault($locale); + } + } catch (\Exception $e) { + + } + } + private function getUrlencodedPrefix($string, $prefix) + { + if (0 !== strpos(rawurldecode($string), $prefix)) { + return false; + } + $len = strlen($prefix); + if (preg_match("#^(%[[:xdigit:]]{2}|.){{$len}}#", $string, $match)) { + return $match[0]; + } + return false; + } +} +namespace Symfony\Component\HttpFoundation; + +class ParameterBag implements \IteratorAggregate, \Countable +{ + protected $parameters; + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + public function all() + { + return $this->parameters; + } + public function keys() + { + return array_keys($this->parameters); + } + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + public function get($path, $default = null, $deep = false) + { + if (!$deep || false === ($pos = strpos($path, '['))) { + return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default; + } + $root = substr($path, 0, $pos); + if (!array_key_exists($root, $this->parameters)) { + return $default; + } + $value = $this->parameters[$root]; + $currentKey = null; + for ($i = $pos, $c = strlen($path); $i < $c; $i++) { + $char = $path[$i]; + if ('[' === $char) { + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i)); + } + $currentKey = ''; + } elseif (']' === $char) { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i)); + } + if (!is_array($value) || !array_key_exists($currentKey, $value)) { + return $default; + } + $value = $value[$currentKey]; + $currentKey = null; + } else { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i)); + } + $currentKey .= $char; + } + } + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Path must end with "]".')); + } + return $value; + } + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + public function remove($key) + { + unset($this->parameters[$key]); + } + public function getAlpha($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default, $deep)); + } + public function getAlnum($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default, $deep)); + } + public function getDigits($key, $default = '', $deep = false) + { + return str_replace(array('-', '+'), '', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT)); + } + public function getInt($key, $default = 0, $deep = false) + { + return (int) $this->get($key, $default, $deep); + } + public function filter($key, $default = null, $deep = false, $filter = FILTER_DEFAULT, $options = array()) + { + $value = $this->get($key, $default, $deep); + if (!is_array($options) && $options) { + $options = array('flags' => $options); + } + if (is_array($value) && !isset($options['flags'])) { + $options['flags'] = FILTER_REQUIRE_ARRAY; + } + return filter_var($value, $filter, $options); + } + public function getIterator() + { + return new \ArrayIterator($this->parameters); + } + public function count() + { + return count($this->parameters); + } +} +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\File\UploadedFile; +class FileBag extends ParameterBag +{ + private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); + public function __construct(array $parameters = array()) + { + $this->replace($parameters); + } + public function replace(array $files = array()) + { + $this->parameters = array(); + $this->add($files); + } + public function set($key, $value) + { + if (!is_array($value) && !$value instanceof UploadedFile) { + throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.'); + } + parent::set($key, $this->convertFileInformation($value)); + } + public function add(array $files = array()) + { + foreach ($files as $key => $file) { + $this->set($key, $file); + } + } + protected function convertFileInformation($file) + { + if ($file instanceof UploadedFile) { + return $file; + } + $file = $this->fixPhpFilesArray($file); + if (is_array($file)) { + $keys = array_keys($file); + sort($keys); + if ($keys == self::$fileKeys) { + if (UPLOAD_ERR_NO_FILE == $file['error']) { + $file = null; + } else { + $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); + } + } else { + $file = array_map(array($this, 'convertFileInformation'), $file); + } + } + return $file; + } + protected function fixPhpFilesArray($data) + { + if (!is_array($data)) { + return $data; + } + $keys = array_keys($data); + sort($keys); + if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) { + return $data; + } + $files = $data; + foreach (self::$fileKeys as $k) { + unset($files[$k]); + } + foreach (array_keys($data['name']) as $key) { + $files[$key] = $this->fixPhpFilesArray(array('error' => $data['error'][$key], 'name' => $data['name'][$key], 'type' => $data['type'][$key], 'tmp_name' => $data['tmp_name'][$key], 'size' => $data['size'][$key])); + } + return $files; + } +} +namespace Symfony\Component\HttpFoundation; + +class ServerBag extends ParameterBag +{ + public function getHeaders() + { + $headers = array(); + $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true); + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, 'HTTP_')) { + $headers[substr($key, 5)] = $value; + } elseif (isset($contentHeaders[$key])) { + $headers[$key] = $value; + } + } + if (isset($this->parameters['PHP_AUTH_USER'])) { + $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER']; + $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : ''; + } else { + $authorizationHeader = null; + if (isset($this->parameters['HTTP_AUTHORIZATION'])) { + $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION']; + } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) { + $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION']; + } + if (null !== $authorizationHeader && 0 === stripos($authorizationHeader, 'basic')) { + $exploded = explode(':', base64_decode(substr($authorizationHeader, 6))); + if (count($exploded) == 2) { + list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + } + } + } + if (isset($headers['PHP_AUTH_USER'])) { + $headers['AUTHORIZATION'] = 'Basic ' . base64_encode($headers['PHP_AUTH_USER'] . ':' . $headers['PHP_AUTH_PW']); + } + return $headers; + } +} +namespace Symfony\Component\HttpFoundation; + +class HeaderBag implements \IteratorAggregate, \Countable +{ + protected $headers; + protected $cacheControl; + public function __construct(array $headers = array()) + { + $this->cacheControl = array(); + $this->headers = array(); + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + public function __toString() + { + if (!$this->headers) { + return ''; + } + $max = max(array_map('strlen', array_keys($this->headers))) + 1; + $content = ''; + ksort($this->headers); + foreach ($this->headers as $name => $values) { + $name = implode('-', array_map('ucfirst', explode('-', $name))); + foreach ($values as $value) { + $content .= sprintf("%-{$max}s %s\r\n", $name . ':', $value); + } + } + return $content; + } + public function all() + { + return $this->headers; + } + public function keys() + { + return array_keys($this->headers); + } + public function replace(array $headers = array()) + { + $this->headers = array(); + $this->add($headers); + } + public function add(array $headers) + { + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + public function get($key, $default = null, $first = true) + { + $key = strtr(strtolower($key), '_', '-'); + if (!array_key_exists($key, $this->headers)) { + if (null === $default) { + return $first ? null : array(); + } + return $first ? $default : array($default); + } + if ($first) { + return count($this->headers[$key]) ? $this->headers[$key][0] : $default; + } + return $this->headers[$key]; + } + public function set($key, $values, $replace = true) + { + $key = strtr(strtolower($key), '_', '-'); + $values = array_values((array) $values); + if (true === $replace || !isset($this->headers[$key])) { + $this->headers[$key] = $values; + } else { + $this->headers[$key] = array_merge($this->headers[$key], $values); + } + if ('cache-control' === $key) { + $this->cacheControl = $this->parseCacheControl($values[0]); + } + } + public function has($key) + { + return array_key_exists(strtr(strtolower($key), '_', '-'), $this->headers); + } + public function contains($key, $value) + { + return in_array($value, $this->get($key, null, false)); + } + public function remove($key) + { + $key = strtr(strtolower($key), '_', '-'); + unset($this->headers[$key]); + if ('cache-control' === $key) { + $this->cacheControl = array(); + } + } + public function getDate($key, \DateTime $default = null) + { + if (null === ($value = $this->get($key))) { + return $default; + } + if (false === ($date = \DateTime::createFromFormat(DATE_RFC2822, $value))) { + throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value)); + } + return $date; + } + public function addCacheControlDirective($key, $value = true) + { + $this->cacheControl[$key] = $value; + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl); + } + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; + } + public function removeCacheControlDirective($key) + { + unset($this->cacheControl[$key]); + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + public function getIterator() + { + return new \ArrayIterator($this->headers); + } + public function count() + { + return count($this->headers); + } + protected function getCacheControlHeader() + { + $parts = array(); + ksort($this->cacheControl); + foreach ($this->cacheControl as $key => $value) { + if (true === $value) { + $parts[] = $key; + } else { + if (preg_match('#[^a-zA-Z0-9._-]#', $value)) { + $value = '"' . $value . '"'; + } + $parts[] = "{$key}={$value}"; + } + } + return implode(', ', $parts); + } + protected function parseCacheControl($header) + { + $cacheControl = array(); + preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\\s*(?:=(?:"([^"]*)"|([^ \\t",;]*)))?#', $header, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true); + } + return $cacheControl; + } +} +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; +interface SessionInterface +{ + public function start(); + public function getId(); + public function setId($id); + public function getName(); + public function setName($name); + public function invalidate($lifetime = null); + public function migrate($destroy = false, $lifetime = null); + public function save(); + public function has($name); + public function get($name, $default = null); + public function set($name, $value); + public function all(); + public function replace(array $attributes); + public function remove($name); + public function clear(); + public function isStarted(); + public function registerBag(SessionBagInterface $bag); + public function getBag($name); + public function getMetadataBag(); +} +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; +interface SessionStorageInterface +{ + public function start(); + public function isStarted(); + public function getId(); + public function setId($id); + public function getName(); + public function setName($name); + public function regenerate($destroy = false, $lifetime = null); + public function save(); + public function clear(); + public function getBag($name); + public function registerBag(SessionBagInterface $bag); + public function getMetadataBag(); +} +namespace Symfony\Component\HttpFoundation\Session; + +interface SessionBagInterface +{ + public function getName(); + public function initialize(array &$array); + public function getStorageKey(); + public function clear(); +} +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +interface AttributeBagInterface extends SessionBagInterface +{ + public function has($name); + public function get($name, $default = null); + public function set($name, $value); + public function all(); + public function replace(array $attributes); + public function remove($name); +} +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +class Session implements SessionInterface, \IteratorAggregate, \Countable +{ + protected $storage; + private $flashName; + private $attributeName; + public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) + { + $this->storage = $storage ?: new NativeSessionStorage(); + $attributes = $attributes ?: new AttributeBag(); + $this->attributeName = $attributes->getName(); + $this->registerBag($attributes); + $flashes = $flashes ?: new FlashBag(); + $this->flashName = $flashes->getName(); + $this->registerBag($flashes); + } + public function start() + { + return $this->storage->start(); + } + public function has($name) + { + return $this->storage->getBag($this->attributeName)->has($name); + } + public function get($name, $default = null) + { + return $this->storage->getBag($this->attributeName)->get($name, $default); + } + public function set($name, $value) + { + $this->storage->getBag($this->attributeName)->set($name, $value); + } + public function all() + { + return $this->storage->getBag($this->attributeName)->all(); + } + public function replace(array $attributes) + { + $this->storage->getBag($this->attributeName)->replace($attributes); + } + public function remove($name) + { + return $this->storage->getBag($this->attributeName)->remove($name); + } + public function clear() + { + $this->storage->getBag($this->attributeName)->clear(); + } + public function isStarted() + { + return $this->storage->isStarted(); + } + public function getIterator() + { + return new \ArrayIterator($this->storage->getBag($this->attributeName)->all()); + } + public function count() + { + return count($this->storage->getBag($this->attributeName)->all()); + } + public function invalidate($lifetime = null) + { + $this->storage->clear(); + return $this->migrate(true, $lifetime); + } + public function migrate($destroy = false, $lifetime = null) + { + return $this->storage->regenerate($destroy, $lifetime); + } + public function save() + { + $this->storage->save(); + } + public function getId() + { + return $this->storage->getId(); + } + public function setId($id) + { + $this->storage->setId($id); + } + public function getName() + { + return $this->storage->getName(); + } + public function setName($name) + { + $this->storage->setName($name); + } + public function getMetadataBag() + { + return $this->storage->getMetadataBag(); + } + public function registerBag(SessionBagInterface $bag) + { + $this->storage->registerBag($bag); + } + public function getBag($name) + { + return $this->storage->getBag($name); + } + public function getFlashBag() + { + return $this->getBag($this->flashName); + } +} +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; +class NativeSessionStorage implements SessionStorageInterface +{ + protected $bags; + protected $started = false; + protected $closed = false; + protected $saveHandler; + protected $metadataBag; + public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null) + { + session_cache_limiter(''); + ini_set('session.use_cookies', 1); + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_register_shutdown(); + } else { + register_shutdown_function('session_write_close'); + } + $this->setMetadataBag($metaBag); + $this->setOptions($options); + $this->setSaveHandler($handler); + } + public function getSaveHandler() + { + return $this->saveHandler; + } + public function start() + { + if ($this->started && !$this->closed) { + return true; + } + if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) { + throw new \RuntimeException('Failed to start the session: already started by PHP.'); + } + if (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) { + throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).'); + } + if (ini_get('session.use_cookies') && headers_sent($file, $line)) { + throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line)); + } + if (!session_start()) { + throw new \RuntimeException('Failed to start the session'); + } + $this->loadSession(); + if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { + $this->saveHandler->setActive(true); + } + return true; + } + public function getId() + { + if (!$this->started) { + return ''; + } + return $this->saveHandler->getId(); + } + public function setId($id) + { + $this->saveHandler->setId($id); + } + public function getName() + { + return $this->saveHandler->getName(); + } + public function setName($name) + { + $this->saveHandler->setName($name); + } + public function regenerate($destroy = false, $lifetime = null) + { + if (null !== $lifetime) { + ini_set('session.cookie_lifetime', $lifetime); + } + if ($destroy) { + $this->metadataBag->stampNew(); + } + return session_regenerate_id($destroy); + } + public function save() + { + session_write_close(); + if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { + $this->saveHandler->setActive(false); + } + $this->closed = true; + } + public function clear() + { + foreach ($this->bags as $bag) { + $bag->clear(); + } + $_SESSION = array(); + $this->loadSession(); + } + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + if ($this->saveHandler->isActive() && !$this->started) { + $this->loadSession(); + } elseif (!$this->started) { + $this->start(); + } + return $this->bags[$name]; + } + public function setMetadataBag(MetadataBag $metaBag = null) + { + if (null === $metaBag) { + $metaBag = new MetadataBag(); + } + $this->metadataBag = $metaBag; + } + public function getMetadataBag() + { + return $this->metadataBag; + } + public function isStarted() + { + return $this->started; + } + public function setOptions(array $options) + { + $validOptions = array_flip(array('cache_limiter', 'cookie_domain', 'cookie_httponly', 'cookie_lifetime', 'cookie_path', 'cookie_secure', 'entropy_file', 'entropy_length', 'gc_divisor', 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', 'hash_function', 'name', 'referer_check', 'serialize_handler', 'use_cookies', 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags')); + foreach ($options as $key => $value) { + if (isset($validOptions[$key])) { + ini_set('session.' . $key, $value); + } + } + } + public function setSaveHandler($saveHandler = null) + { + if (!$saveHandler instanceof AbstractProxy && !$saveHandler instanceof NativeSessionHandler && !$saveHandler instanceof \SessionHandlerInterface && null !== $saveHandler) { + throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \\SessionHandlerInterface; or be null.'); + } + if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) { + $saveHandler = new SessionHandlerProxy($saveHandler); + } elseif (!$saveHandler instanceof AbstractProxy) { + $saveHandler = version_compare(phpversion(), '5.4.0', '>=') ? new SessionHandlerProxy(new \SessionHandler()) : new NativeProxy(); + } + $this->saveHandler = $saveHandler; + if ($this->saveHandler instanceof \SessionHandlerInterface) { + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_set_save_handler($this->saveHandler, false); + } else { + session_set_save_handler(array($this->saveHandler, 'open'), array($this->saveHandler, 'close'), array($this->saveHandler, 'read'), array($this->saveHandler, 'write'), array($this->saveHandler, 'destroy'), array($this->saveHandler, 'gc')); + } + } + } + protected function loadSession(array &$session = null) + { + if (null === $session) { + $session =& $_SESSION; + } + $bags = array_merge($this->bags, array($this->metadataBag)); + foreach ($bags as $bag) { + $key = $bag->getStorageKey(); + $session[$key] = isset($session[$key]) ? $session[$key] : array(); + $bag->initialize($session[$key]); + } + $this->started = true; + $this->closed = false; + } +} +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable +{ + private $name = 'attributes'; + private $storageKey; + protected $attributes = array(); + public function __construct($storageKey = '_sf2_attributes') + { + $this->storageKey = $storageKey; + } + public function getName() + { + return $this->name; + } + public function setName($name) + { + $this->name = $name; + } + public function initialize(array &$attributes) + { + $this->attributes =& $attributes; + } + public function getStorageKey() + { + return $this->storageKey; + } + public function has($name) + { + return array_key_exists($name, $this->attributes); + } + public function get($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + public function set($name, $value) + { + $this->attributes[$name] = $value; + } + public function all() + { + return $this->attributes; + } + public function replace(array $attributes) + { + $this->attributes = array(); + foreach ($attributes as $key => $value) { + $this->set($key, $value); + } + } + public function remove($name) + { + $retval = null; + if (array_key_exists($name, $this->attributes)) { + $retval = $this->attributes[$name]; + unset($this->attributes[$name]); + } + return $retval; + } + public function clear() + { + $return = $this->attributes; + $this->attributes = array(); + return $return; + } + public function getIterator() + { + return new \ArrayIterator($this->attributes); + } + public function count() + { + return count($this->attributes); + } +} +namespace Symfony\Component\HttpFoundation\Session\Flash; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +interface FlashBagInterface extends SessionBagInterface +{ + public function add($type, $message); + public function set($type, $message); + public function peek($type, array $default = array()); + public function peekAll(); + public function get($type, array $default = array()); + public function all(); + public function setAll(array $messages); + public function has($type); + public function keys(); +} +namespace Symfony\Component\HttpFoundation\Session\Flash; + +class FlashBag implements FlashBagInterface, \IteratorAggregate +{ + private $name = 'flashes'; + private $flashes = array(); + private $storageKey; + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + } + public function getName() + { + return $this->name; + } + public function setName($name) + { + $this->name = $name; + } + public function initialize(array &$flashes) + { + $this->flashes =& $flashes; + } + public function add($type, $message) + { + $this->flashes[$type][] = $message; + } + public function peek($type, array $default = array()) + { + return $this->has($type) ? $this->flashes[$type] : $default; + } + public function peekAll() + { + return $this->flashes; + } + public function get($type, array $default = array()) + { + if (!$this->has($type)) { + return $default; + } + $return = $this->flashes[$type]; + unset($this->flashes[$type]); + return $return; + } + public function all() + { + $return = $this->peekAll(); + $this->flashes = array(); + return $return; + } + public function set($type, $messages) + { + $this->flashes[$type] = (array) $messages; + } + public function setAll(array $messages) + { + $this->flashes = $messages; + } + public function has($type) + { + return array_key_exists($type, $this->flashes) && $this->flashes[$type]; + } + public function keys() + { + return array_keys($this->flashes); + } + public function getStorageKey() + { + return $this->storageKey; + } + public function clear() + { + return $this->all(); + } + public function getIterator() + { + return new \ArrayIterator($this->all()); + } +} +namespace Symfony\Component\HttpFoundation\Session\Flash; + +class AutoExpireFlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + private $flashes = array(); + private $storageKey; + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + $this->flashes = array('display' => array(), 'new' => array()); + } + public function getName() + { + return $this->name; + } + public function setName($name) + { + $this->name = $name; + } + public function initialize(array &$flashes) + { + $this->flashes =& $flashes; + $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array(); + $this->flashes['new'] = array(); + } + public function add($type, $message) + { + $this->flashes['new'][$type][] = $message; + } + public function peek($type, array $default = array()) + { + return $this->has($type) ? $this->flashes['display'][$type] : $default; + } + public function peekAll() + { + return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array(); + } + public function get($type, array $default = array()) + { + $return = $default; + if (!$this->has($type)) { + return $return; + } + if (isset($this->flashes['display'][$type])) { + $return = $this->flashes['display'][$type]; + unset($this->flashes['display'][$type]); + } + return $return; + } + public function all() + { + $return = $this->flashes['display']; + $this->flashes = array('new' => array(), 'display' => array()); + return $return; + } + public function setAll(array $messages) + { + $this->flashes['new'] = $messages; + } + public function set($type, $messages) + { + $this->flashes['new'][$type] = (array) $messages; + } + public function has($type) + { + return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type]; + } + public function keys() + { + return array_keys($this->flashes['display']); + } + public function getStorageKey() + { + return $this->storageKey; + } + public function clear() + { + return $this->all(); + } +} +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +class MetadataBag implements SessionBagInterface +{ + const CREATED = 'c'; + const UPDATED = 'u'; + const LIFETIME = 'l'; + private $name = '__metadata'; + private $storageKey; + protected $meta = array(); + private $lastUsed; + public function __construct($storageKey = '_sf2_meta') + { + $this->storageKey = $storageKey; + $this->meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0); + } + public function initialize(array &$array) + { + $this->meta =& $array; + if (isset($array[self::CREATED])) { + $this->lastUsed = $this->meta[self::UPDATED]; + $this->meta[self::UPDATED] = time(); + } else { + $this->stampCreated(); + } + } + public function getLifetime() + { + return $this->meta[self::LIFETIME]; + } + public function stampNew($lifetime = null) + { + $this->stampCreated($lifetime); + } + public function getStorageKey() + { + return $this->storageKey; + } + public function getCreated() + { + return $this->meta[self::CREATED]; + } + public function getLastUsed() + { + return $this->lastUsed; + } + public function clear() + { + + } + public function getName() + { + return $this->name; + } + public function setName($name) + { + $this->name = $name; + } + private function stampCreated($lifetime = null) + { + $timeStamp = time(); + $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp; + $this->meta[self::LIFETIME] = null === $lifetime ? ini_get('session.cookie_lifetime') : $lifetime; + } +} +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +if (version_compare(phpversion(), '5.4.0', '>=')) { + class NativeSessionHandler extends \SessionHandler + { + + } +} else { + class NativeSessionHandler + { + + } +} +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +abstract class AbstractProxy +{ + protected $wrapper = false; + protected $active = false; + protected $saveHandlerName; + public function getSaveHandlerName() + { + return $this->saveHandlerName; + } + public function isSessionHandlerInterface() + { + return $this instanceof \SessionHandlerInterface; + } + public function isWrapper() + { + return $this->wrapper; + } + public function isActive() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + return $this->active = \PHP_SESSION_ACTIVE === session_status(); + } + return $this->active; + } + public function setActive($flag) + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + throw new \LogicException('This method is disabled in PHP 5.4.0+'); + } + $this->active = (bool) $flag; + } + public function getId() + { + return session_id(); + } + public function setId($id) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the ID of an active session'); + } + session_id($id); + } + public function getName() + { + return session_name(); + } + public function setName($name) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the name of an active session'); + } + session_name($name); + } +} +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface +{ + protected $handler; + public function __construct(\SessionHandlerInterface $handler) + { + $this->handler = $handler; + $this->wrapper = $handler instanceof \SessionHandler; + $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user'; + } + public function open($savePath, $sessionName) + { + $return = (bool) $this->handler->open($savePath, $sessionName); + if (true === $return) { + $this->active = true; + } + return $return; + } + public function close() + { + $this->active = false; + return (bool) $this->handler->close(); + } + public function read($id) + { + return (string) $this->handler->read($id); + } + public function write($id, $data) + { + return (bool) $this->handler->write($id, $data); + } + public function destroy($id) + { + return (bool) $this->handler->destroy($id); + } + public function gc($maxlifetime) + { + return (bool) $this->handler->gc($maxlifetime); + } +} +namespace Symfony\Component\HttpFoundation; + +class AcceptHeaderItem +{ + private $value; + private $quality = 1.0; + private $index = 0; + private $attributes = array(); + public function __construct($value, array $attributes = array()) + { + $this->value = $value; + foreach ($attributes as $name => $value) { + $this->setAttribute($name, $value); + } + } + public static function fromString($itemValue) + { + $bits = preg_split('/\\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $value = array_shift($bits); + $attributes = array(); + $lastNullAttribute = null; + foreach ($bits as $bit) { + if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ($start === '"' || $start === '\'')) { + $attributes[$lastNullAttribute] = substr($bit, 1, -1); + } elseif ('=' === $end) { + $lastNullAttribute = $bit = substr($bit, 0, -1); + $attributes[$bit] = null; + } else { + $parts = explode('=', $bit); + $attributes[$parts[0]] = isset($parts[1]) && strlen($parts[1]) > 0 ? $parts[1] : ''; + } + } + return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ($start === '"' || $start === '\'') ? substr($value, 1, -1) : $value, $attributes); + } + public function __toString() + { + $string = $this->value . ($this->quality < 1 ? ';q=' . $this->quality : ''); + if (count($this->attributes) > 0) { + $string .= ';' . implode(';', array_map(function ($name, $value) { + return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value); + }, array_keys($this->attributes), $this->attributes)); + } + return $string; + } + public function setValue($value) + { + $this->value = $value; + return $this; + } + public function getValue() + { + return $this->value; + } + public function setQuality($quality) + { + $this->quality = $quality; + return $this; + } + public function getQuality() + { + return $this->quality; + } + public function setIndex($index) + { + $this->index = $index; + return $this; + } + public function getIndex() + { + return $this->index; + } + public function hasAttribute($name) + { + return isset($this->attributes[$name]); + } + public function getAttribute($name, $default = null) + { + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; + } + public function getAttributes() + { + return $this->attributes; + } + public function setAttribute($name, $value) + { + if ('q' === $name) { + $this->quality = (double) $value; + } else { + $this->attributes[$name] = (string) $value; + } + return $this; + } +} +namespace Symfony\Component\HttpFoundation; + +class AcceptHeader +{ + private $items = array(); + private $sorted = true; + public function __construct(array $items) + { + foreach ($items as $item) { + $this->add($item); + } + } + public static function fromString($headerValue) + { + $index = 0; + return new self(array_map(function ($itemValue) use(&$index) { + $item = AcceptHeaderItem::fromString($itemValue); + $item->setIndex($index++); + return $item; + }, preg_split('/\\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE))); + } + public function __toString() + { + return implode(',', $this->items); + } + public function has($value) + { + return isset($this->items[$value]); + } + public function get($value) + { + return isset($this->items[$value]) ? $this->items[$value] : null; + } + public function add(AcceptHeaderItem $item) + { + $this->items[$item->getValue()] = $item; + $this->sorted = false; + return $this; + } + public function all() + { + $this->sort(); + return $this->items; + } + public function filter($pattern) + { + return new self(array_filter($this->items, function (AcceptHeaderItem $item) use($pattern) { + return preg_match($pattern, $item->getValue()); + })); + } + public function first() + { + $this->sort(); + return !empty($this->items) ? reset($this->items) : null; + } + private function sort() + { + if (!$this->sorted) { + uasort($this->items, function ($a, $b) { + $qA = $a->getQuality(); + $qB = $b->getQuality(); + if ($qA === $qB) { + return $a->getIndex() > $b->getIndex() ? 1 : -1; + } + return $qA > $qB ? -1 : 1; + }); + $this->sorted = true; + } + } +} +namespace Symfony\Component\Debug; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Debug\Exception\FlattenException; +if (!defined('ENT_SUBSTITUTE')) { + define('ENT_SUBSTITUTE', 8); +} +class ExceptionHandler +{ + private $debug; + private $charset; + public function __construct($debug = true, $charset = 'UTF-8') + { + $this->debug = $debug; + $this->charset = $charset; + } + public static function register($debug = true) + { + $handler = new static($debug); + set_exception_handler(array($handler, 'handle')); + return $handler; + } + public function handle(\Exception $exception) + { + if (class_exists('Symfony\\Component\\HttpFoundation\\Response')) { + $this->createResponse($exception)->send(); + } else { + $this->sendPhpResponse($exception); + } + } + public function sendPhpResponse($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); + foreach ($exception->getHeaders() as $name => $value) { + header($name . ': ' . $value, false); + } + echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + public function createResponse($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + return new Response($this->decorate($this->getContent($exception), $this->getStylesheet($exception)), $exception->getStatusCode(), $exception->getHeaders()); + } + public function getContent(FlattenException $exception) + { + switch ($exception->getStatusCode()) { + case 404: + $title = 'Sorry, the page you are looking for could not be found.'; + break; + default: + $title = 'Whoops, looks like something went wrong.'; + } + $content = ''; + if ($this->debug) { + try { + $count = count($exception->getAllPrevious()); + $total = $count + 1; + foreach ($exception->toArray() as $position => $e) { + $ind = $count - $position + 1; + $class = $this->abbrClass($e['class']); + $message = nl2br($e['message']); + $content .= sprintf('
+

%d/%d %s: %s

+
+
+
    +', $ind, $total, $class, $message); + foreach ($e['trace'] as $trace) { + $content .= '
  1. '; + if ($trace['function']) { + $content .= sprintf('at %s%s%s(%s)', $this->abbrClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); + } + if (isset($trace['file']) && isset($trace['line'])) { + if ($linkFormat = ini_get('xdebug.file_link_format')) { + $link = str_replace(array('%f', '%l'), array($trace['file'], $trace['line']), $linkFormat); + $content .= sprintf(' in %s line %s', $link, $trace['file'], $trace['line']); + } else { + $content .= sprintf(' in %s line %s', $trace['file'], $trace['line']); + } + } + $content .= '
  2. +'; + } + $content .= '
+
+'; + } + } catch (\Exception $e) { + if ($this->debug) { + $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($exception), $exception->getMessage()); + } else { + $title = 'Whoops, looks like something went wrong.'; + } + } + } + return "
\r\n

{$title}

\r\n {$content}\r\n
"; + } + public function getStylesheet(FlattenException $exception) + { + return ' .sf-reset { font: 11px Verdana, Arial, sans-serif; color: #333 } + .sf-reset .clear { clear:both; height:0; font-size:0; line-height:0; } + .sf-reset .clear_fix:after { display:block; height:0; clear:both; visibility:hidden; } + .sf-reset .clear_fix { display:inline-block; } + .sf-reset * html .clear_fix { height:1%; } + .sf-reset .clear_fix { display:block; } + .sf-reset, .sf-reset .block { margin: auto } + .sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; } + .sf-reset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px } + .sf-reset strong { font-weight:bold; } + .sf-reset a { color:#6c6159; } + .sf-reset a img { border:none; } + .sf-reset a:hover { text-decoration:underline; } + .sf-reset em { font-style:italic; } + .sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif } + .sf-reset h2 span { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; } + .sf-reset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; } + .sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px; + -webkit-border-bottom-right-radius: 16px; + -webkit-border-bottom-left-radius: 16px; + -moz-border-radius-bottomright: 16px; + -moz-border-radius-bottomleft: 16px; + border-bottom-right-radius: 16px; + border-bottom-left-radius: 16px; + border-bottom:1px solid #ccc; + border-right:1px solid #ccc; + border-left:1px solid #ccc; + } + .sf-reset .block_exception { background-color:#ddd; color: #333; padding:20px; + -webkit-border-top-left-radius: 16px; + -webkit-border-top-right-radius: 16px; + -moz-border-radius-topleft: 16px; + -moz-border-radius-topright: 16px; + border-top-left-radius: 16px; + border-top-right-radius: 16px; + border-top:1px solid #ccc; + border-right:1px solid #ccc; + border-left:1px solid #ccc; + overflow: hidden; + word-wrap: break-word; + } + .sf-reset li a { background:none; color:#868686; text-decoration:none; } + .sf-reset li a:hover { background:none; color:#313131; text-decoration:underline; } + .sf-reset ol { padding: 10px 0; } + .sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + border: 1px solid #ccc; + }'; + } + private function decorate($content, $css) + { + return "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n {$content}\r\n \r\n"; + } + private function abbrClass($class) + { + $parts = explode('\\', $class); + return sprintf('%s', $class, array_pop($parts)); + } + private function formatArgs(array $args) + { + $result = array(); + foreach ($args as $key => $item) { + if ('object' === $item[0]) { + $formattedValue = sprintf('object(%s)', $this->abbrClass($item[1])); + } elseif ('array' === $item[0]) { + $formattedValue = sprintf('array(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + } elseif ('string' === $item[0]) { + $formattedValue = sprintf('\'%s\'', htmlspecialchars($item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset)); + } elseif ('null' === $item[0]) { + $formattedValue = 'null'; + } elseif ('boolean' === $item[0]) { + $formattedValue = '' . strtolower(var_export($item[1], true)) . ''; + } elseif ('resource' === $item[0]) { + $formattedValue = 'resource'; + } else { + $formattedValue = str_replace(' +', '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), true)); + } + $result[] = is_int($key) ? $formattedValue : sprintf('\'%s\' => %s', $key, $formattedValue); + } + return implode(', ', $result); + } +} +namespace Illuminate\Support; + +use ReflectionClass; +abstract class ServiceProvider +{ + protected $app; + protected $defer = false; + public function __construct($app) + { + $this->app = $app; + } + public function boot() + { + + } + public abstract function register(); + public function package($package, $namespace = null, $path = null) + { + $namespace = $this->getPackageNamespace($package, $namespace); + $path = $path ?: $this->guessPackagePath(); + $config = $path . '/config'; + if ($this->app['files']->isDirectory($config)) { + $this->app['config']->package($package, $config, $namespace); + } + $lang = $path . '/lang'; + if ($this->app['files']->isDirectory($lang)) { + $this->app['translator']->addNamespace($namespace, $lang); + } + $appView = $this->getAppViewPath($package, $namespace); + if ($this->app['files']->isDirectory($appView)) { + $this->app['view']->addNamespace($namespace, $appView); + } + $view = $path . '/views'; + if ($this->app['files']->isDirectory($view)) { + $this->app['view']->addNamespace($namespace, $view); + } + } + public function guessPackagePath() + { + $reflect = new ReflectionClass($this); + $chain = $this->getClassChain($reflect); + $path = $chain[count($chain) - 2]->getFileName(); + return realpath(dirname($path) . '/../../'); + } + protected function getClassChain(ReflectionClass $reflect) + { + $lastName = null; + while ($reflect !== false) { + $classes[] = $reflect; + $reflect = $reflect->getParentClass(); + } + return $classes; + } + protected function getPackageNamespace($package, $namespace) + { + if (is_null($namespace)) { + list($vendor, $namespace) = explode('/', $package); + } + return $namespace; + } + public function commands() + { + $commands = func_get_args(); + $events = $this->app['events']; + $events->listen('artisan.start', function ($artisan) use($commands) { + $artisan->resolveCommands($commands); + }); + } + protected function getAppViewPath($package, $namespace) + { + return $this->app['path'] . "/views/packages/{$package}/{$namespace}"; + } + public function provides() + { + return array(); + } + public function isDeferred() + { + return $this->defer; + } +} +namespace Illuminate\Exception; + +use Closure; +use Whoops\Handler\PrettyPageHandler; +use Whoops\Handler\JsonResponseHandler; +use Illuminate\Support\ServiceProvider; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Debug\ExceptionHandler as KernelHandler; +class ExceptionServiceProvider extends ServiceProvider +{ + public function register() + { + $this->registerDisplayers(); + $this->registerHandler(); + } + protected function registerDisplayers() + { + $this->registerPlainDisplayer(); + $this->registerDebugDisplayer(); + } + protected function registerHandler() + { + $this->app['exception'] = $this->app->share(function ($app) { + return new Handler($app, $app['exception.plain'], $app['exception.debug']); + }); + } + protected function registerPlainDisplayer() + { + $this->app['exception.plain'] = $this->app->share(function ($app) { + $handler = new KernelHandler($app['config']['app.debug']); + return new SymfonyDisplayer($handler); + }); + } + protected function registerDebugDisplayer() + { + $this->registerWhoops(); + $this->app['exception.debug'] = $this->app->share(function ($app) { + return new WhoopsDisplayer($app['whoops'], $app->runningInConsole()); + }); + } + protected function registerWhoops() + { + $this->registerWhoopsHandler(); + $this->app['whoops'] = $this->app->share(function ($app) { + with($whoops = new \Whoops\Run())->allowQuit(false); + return $whoops->pushHandler($app['whoops.handler']); + }); + } + protected function registerWhoopsHandler() + { + if ($this->shouldReturnJson()) { + $this->app['whoops.handler'] = $this->app->share(function () { + return new JsonResponseHandler(); + }); + } else { + $this->registerPrettyWhoopsHandler(); + } + } + protected function shouldReturnJson() + { + $definitely = ($this->app['request']->ajax() or $this->app->runningInConsole()); + return $definitely or $this->app['request']->wantsJson(); + } + protected function registerPrettyWhoopsHandler() + { + $me = $this; + $this->app['whoops.handler'] = $this->app->share(function () use($me) { + with($handler = new PrettyPageHandler())->setEditor('sublime'); + if (!is_null($path = $me->resourcePath())) { + $handler->setResourcesPath($path); + } + return $handler; + }); + } + public function resourcePath() + { + if (is_dir($path = $this->getResourcePath())) { + return $path; + } + } + protected function getResourcePath() + { + $base = $this->app['path.base']; + return $base . '/vendor/laravel/framework/src/Illuminate/Exception/resources'; + } +} +namespace Illuminate\Routing; + +use Illuminate\Support\ServiceProvider; +class RoutingServiceProvider extends ServiceProvider +{ + public function register() + { + $this->registerRouter(); + $this->registerUrlGenerator(); + $this->registerRedirector(); + } + protected function registerRouter() + { + $this->app['router'] = $this->app->share(function ($app) { + $router = new Router($app); + if ($app['env'] == 'testing') { + $router->disableFilters(); + } + return $router; + }); + } + protected function registerUrlGenerator() + { + $this->app['url'] = $this->app->share(function ($app) { + $routes = $app['router']->getRoutes(); + return new UrlGenerator($routes, $app['request']); + }); + } + protected function registerRedirector() + { + $this->app['redirect'] = $this->app->share(function ($app) { + $redirector = new Redirector($app['url']); + if (isset($app['session'])) { + $redirector->setSession($app['session']); + } + return $redirector; + }); + } +} +namespace Illuminate\Events; + +use Illuminate\Support\ServiceProvider; +class EventServiceProvider extends ServiceProvider +{ + public function register() + { + $this->app['events'] = $this->app->share(function ($app) { + return new Dispatcher($app); + }); + } +} +namespace Illuminate\Support\Facades; + +use Mockery\MockInterface; +abstract class Facade +{ + protected static $app; + protected static $resolvedInstance; + public static function swap($instance) + { + static::$resolvedInstance[static::getFacadeAccessor()] = $instance; + static::$app->instance(static::getFacadeAccessor(), $instance); + } + public static function shouldReceive() + { + $name = static::getFacadeAccessor(); + if (static::isMock()) { + $mock = static::$resolvedInstance[$name]; + } else { + static::$resolvedInstance[$name] = $mock = \Mockery::mock(static::getMockableClass($name)); + static::$app->instance($name, $mock); + } + return call_user_func_array(array($mock, 'shouldReceive'), func_get_args()); + } + protected static function isMock() + { + $name = static::getFacadeAccessor(); + return isset(static::$resolvedInstance[$name]) and static::$resolvedInstance[$name] instanceof MockInterface; + } + protected static function getMockableClass() + { + return get_class(static::getFacadeRoot()); + } + public static function getFacadeRoot() + { + return static::resolveFacadeInstance(static::getFacadeAccessor()); + } + protected static function getFacadeAccessor() + { + throw new \RuntimeException('Facade does not implement getFacadeAccessor method.'); + } + protected static function resolveFacadeInstance($name) + { + if (is_object($name)) { + return $name; + } + if (isset(static::$resolvedInstance[$name])) { + return static::$resolvedInstance[$name]; + } + return static::$resolvedInstance[$name] = static::$app[$name]; + } + public static function clearResolvedInstance($name) + { + unset(static::$resolvedInstance[$name]); + } + public static function clearResolvedInstances() + { + static::$resolvedInstance = array(); + } + public static function getFacadeApplication() + { + return static::$app; + } + public static function setFacadeApplication($app) + { + static::$app = $app; + } + public static function __callStatic($method, $args) + { + $instance = static::resolveFacadeInstance(static::getFacadeAccessor()); + switch (count($args)) { + case 0: + return $instance->{$method}(); + case 1: + return $instance->{$method}($args[0]); + case 2: + return $instance->{$method}($args[0], $args[1]); + case 3: + return $instance->{$method}($args[0], $args[1], $args[2]); + case 4: + return $instance->{$method}($args[0], $args[1], $args[2], $args[3]); + default: + return call_user_func_array(array($instance, $method), $args); + } + } +} +namespace Illuminate\Support; + +class Str +{ + protected static $macros = array(); + public static function ascii($value) + { + return \Patchwork\Utf8::toAscii($value); + } + public static function camel($value) + { + return lcfirst(static::studly($value)); + } + public static function contains($haystack, $needle) + { + foreach ((array) $needle as $n) { + if (strpos($haystack, $n) !== false) { + return true; + } + } + return false; + } + public static function endsWith($haystack, $needles) + { + foreach ((array) $needles as $needle) { + if ($needle == substr($haystack, strlen($haystack) - strlen($needle))) { + return true; + } + } + return false; + } + public static function finish($value, $cap) + { + return rtrim($value, $cap) . $cap; + } + public static function is($pattern, $value) + { + if ($pattern == $value) { + return true; + } + $pattern = preg_quote($pattern, '#'); + if ($pattern !== '/') { + $pattern = str_replace('\\*', '.*', $pattern) . '\\z'; + } else { + $pattern = '/$'; + } + return (bool) preg_match('#^' . $pattern . '#', $value); + } + public static function length($value) + { + return mb_strlen($value); + } + public static function limit($value, $limit = 100, $end = '...') + { + if (mb_strlen($value) <= $limit) { + return $value; + } + return mb_substr($value, 0, $limit, 'UTF-8') . $end; + } + public static function lower($value) + { + return mb_strtolower($value); + } + public static function words($value, $words = 100, $end = '...') + { + preg_match('/^\\s*+(?:\\S++\\s*+){1,' . $words . '}/u', $value, $matches); + if (!isset($matches[0])) { + return $value; + } + if (strlen($value) == strlen($matches[0])) { + return $value; + } + return rtrim($matches[0]) . $end; + } + public static function plural($value, $count = 2) + { + return Pluralizer::plural($value, $count); + } + public static function random($length = 16) + { + if (function_exists('openssl_random_pseudo_bytes')) { + $bytes = openssl_random_pseudo_bytes($length * 2); + if ($bytes === false) { + throw new \RuntimeException('Unable to generate random string.'); + } + return substr(str_replace(array('/', '+', '='), '', base64_encode($bytes)), 0, $length); + } + return static::quickRandom($length); + } + public static function quickRandom($length = 16) + { + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + return substr(str_shuffle(str_repeat($pool, 5)), 0, $length); + } + public static function upper($value) + { + return mb_strtoupper($value); + } + public static function singular($value) + { + return Pluralizer::singular($value); + } + public static function slug($title, $separator = '-') + { + $title = static::ascii($title); + $title = preg_replace('![^' . preg_quote($separator) . '\\pL\\pN\\s]+!u', '', mb_strtolower($title)); + $flip = $separator == '-' ? '_' : '-'; + $title = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title); + $title = preg_replace('![' . preg_quote($separator) . '\\s]+!u', $separator, $title); + return trim($title, $separator); + } + public static function snake($value, $delimiter = '_') + { + $replace = '$1' . $delimiter . '$2'; + return ctype_lower($value) ? $value : strtolower(preg_replace('/(.)([A-Z])/', $replace, $value)); + } + public static function startsWith($haystack, $needles) + { + foreach ((array) $needles as $needle) { + if (strpos($haystack, $needle) === 0) { + return true; + } + } + return false; + } + public static function studly($value) + { + $value = ucwords(str_replace(array('-', '_'), ' ', $value)); + return str_replace(' ', '', $value); + } + public static function macro($name, $macro) + { + static::$macros[$name] = $macro; + } + public static function __callStatic($method, $parameters) + { + if (isset(static::$macros[$method])) { + return call_user_func_array(static::$macros[$method], $parameters); + } + throw new \BadMethodCallException("Method {$method} does not exist."); + } +} +namespace Symfony\Component\Debug; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\ContextErrorException; +use Psr\Log\LoggerInterface; +class ErrorHandler +{ + const TYPE_DEPRECATION = -100; + private $levels = array(E_WARNING => 'Warning', E_NOTICE => 'Notice', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Runtime Notice', E_RECOVERABLE_ERROR => 'Catchable Fatal Error', E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', E_ERROR => 'Error', E_CORE_ERROR => 'Core Error', E_COMPILE_ERROR => 'Compile Error', E_PARSE => 'Parse'); + private $level; + private $reservedMemory; + private $displayErrors; + private static $loggers = array(); + public static function register($level = null, $displayErrors = true) + { + $handler = new static(); + $handler->setLevel($level); + $handler->setDisplayErrors($displayErrors); + ini_set('display_errors', 0); + set_error_handler(array($handler, 'handle')); + register_shutdown_function(array($handler, 'handleFatal')); + $handler->reservedMemory = str_repeat('x', 10240); + return $handler; + } + public function setLevel($level) + { + $this->level = null === $level ? error_reporting() : $level; + } + public function setDisplayErrors($displayErrors) + { + $this->displayErrors = $displayErrors; + } + public static function setLogger(LoggerInterface $logger, $channel = 'deprecation') + { + self::$loggers[$channel] = $logger; + } + public function handle($level, $message, $file = 'unknown', $line = 0, $context = array()) + { + if (0 === $this->level) { + return false; + } + if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) { + if (isset(self::$loggers['deprecation'])) { + if (version_compare(PHP_VERSION, '5.4', '<')) { + $stack = array_map(function ($row) { + unset($row['args']); + return $row; + }, array_slice(debug_backtrace(false), 0, 10)); + } else { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); + } + self::$loggers['deprecation']->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack)); + } + return true; + } + if ($this->displayErrors && error_reporting() & $level && $this->level & $level) { + throw new ContextErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line, $context); + } + return false; + } + public function handleFatal() + { + if (null === ($error = error_get_last())) { + return; + } + unset($this->reservedMemory); + $type = $error['type']; + if (0 === $this->level || !in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) { + return; + } + if (isset(self::$loggers['emergency'])) { + $fatal = array('type' => $type, 'file' => $error['file'], 'line' => $error['line']); + self::$loggers['emergency']->emerg($error['message'], $fatal); + } + if (!$this->displayErrors) { + return; + } + $exceptionHandler = set_exception_handler(function () { + + }); + restore_exception_handler(); + if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) { + $level = isset($this->levels[$type]) ? $this->levels[$type] : $type; + $message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']); + $exception = new FatalErrorException($message, 0, $type, $error['file'], $error['line']); + $exceptionHandler[0]->handle($exception); + } + } +} +namespace Symfony\Component\HttpKernel\Debug; + +use Symfony\Component\Debug\ErrorHandler as DebugErrorHandler; +class ErrorHandler extends DebugErrorHandler +{ + +} +namespace Illuminate\Config; + +use Closure; +use ArrayAccess; +use Illuminate\Support\NamespacedItemResolver; +class Repository extends NamespacedItemResolver implements ArrayAccess +{ + protected $loader; + protected $environment; + protected $items = array(); + protected $packages = array(); + protected $afterLoad = array(); + public function __construct(LoaderInterface $loader, $environment) + { + $this->loader = $loader; + $this->environment = $environment; + } + public function has($key) + { + $default = microtime(true); + return $this->get($key, $default) != $default; + } + public function hasGroup($key) + { + list($namespace, $group, $item) = $this->parseKey($key); + return $this->loader->exists($group, $namespace); + } + public function get($key, $default = null) + { + list($namespace, $group, $item) = $this->parseKey($key); + $collection = $this->getCollection($group, $namespace); + $this->load($group, $namespace, $collection); + return array_get($this->items[$collection], $item, $default); + } + public function set($key, $value) + { + list($namespace, $group, $item) = $this->parseKey($key); + $collection = $this->getCollection($group, $namespace); + $this->load($group, $namespace, $collection); + if (is_null($item)) { + $this->items[$collection] = $value; + } else { + array_set($this->items[$collection], $item, $value); + } + } + protected function load($group, $namespace, $collection) + { + $env = $this->environment; + if (isset($this->items[$collection])) { + return; + } + $items = $this->loader->load($env, $group, $namespace); + if (isset($this->afterLoad[$namespace])) { + $items = $this->callAfterLoad($namespace, $group, $items); + } + $this->items[$collection] = $items; + } + protected function callAfterLoad($namespace, $group, $items) + { + $callback = $this->afterLoad[$namespace]; + return call_user_func($callback, $this, $group, $items); + } + protected function parseNamespacedSegments($key) + { + list($namespace, $item) = explode('::', $key); + if (in_array($namespace, $this->packages)) { + return $this->parsePackageSegments($key, $namespace, $item); + } + return parent::parseNamespacedSegments($key); + } + protected function parsePackageSegments($key, $namespace, $item) + { + $itemSegments = explode('.', $item); + if (!$this->loader->exists($itemSegments[0], $namespace)) { + return array($namespace, 'config', $item); + } + return parent::parseNamespacedSegments($key); + } + public function package($package, $hint, $namespace = null) + { + $namespace = $this->getPackageNamespace($package, $namespace); + $this->packages[] = $namespace; + $this->addNamespace($namespace, $hint); + $this->afterLoading($namespace, function ($me, $group, $items) use($package) { + $env = $me->getEnvironment(); + $loader = $me->getLoader(); + return $loader->cascadePackage($env, $package, $group, $items); + }); + } + protected function getPackageNamespace($package, $namespace) + { + if (is_null($namespace)) { + list($vendor, $namespace) = explode('/', $package); + } + return $namespace; + } + public function afterLoading($namespace, Closure $callback) + { + $this->afterLoad[$namespace] = $callback; + } + protected function getCollection($group, $namespace = null) + { + $namespace = $namespace ?: '*'; + return $namespace . '::' . $group; + } + public function addNamespace($namespace, $hint) + { + return $this->loader->addNamespace($namespace, $hint); + } + public function getNamespaces() + { + return $this->loader->getNamespaces(); + } + public function getLoader() + { + return $this->loader; + } + public function setLoader(LoaderInterface $loader) + { + $this->loader = $loader; + } + public function getEnvironment() + { + return $this->environment; + } + public function getAfterLoadCallbacks() + { + return $this->afterLoad; + } + public function getItems() + { + return $this->items; + } + public function offsetExists($key) + { + return $this->has($key); + } + public function offsetGet($key) + { + return $this->get($key); + } + public function offsetSet($key, $value) + { + $this->set($key, $value); + } + public function offsetUnset($key) + { + $this->set($key, null); + } +} +namespace Illuminate\Support; + +class NamespacedItemResolver +{ + protected $parsed = array(); + public function parseKey($key) + { + if (isset($this->parsed[$key])) { + return $this->parsed[$key]; + } + $segments = explode('.', $key); + if (strpos($key, '::') === false) { + $parsed = $this->parseBasicSegments($segments); + } else { + $parsed = $this->parseNamespacedSegments($key); + } + return $this->parsed[$key] = $parsed; + } + protected function parseBasicSegments(array $segments) + { + $group = $segments[0]; + if (count($segments) == 1) { + return array(null, $group, null); + } else { + $item = implode('.', array_slice($segments, 1)); + return array(null, $group, $item); + } + } + protected function parseNamespacedSegments($key) + { + list($namespace, $item) = explode('::', $key); + $itemSegments = explode('.', $item); + $groupAndItem = array_slice($this->parseBasicSegments($itemSegments), 1); + return array_merge(array($namespace), $groupAndItem); + } + public function setParsedKey($key, $parsed) + { + $this->parsed[$key] = $parsed; + } +} +namespace Illuminate\Config; + +use Illuminate\Filesystem\Filesystem; +class FileLoader implements LoaderInterface +{ + protected $files; + protected $defaultPath; + protected $hints = array(); + protected $exists = array(); + public function __construct(Filesystem $files, $defaultPath) + { + $this->files = $files; + $this->defaultPath = $defaultPath; + } + public function load($environment, $group, $namespace = null) + { + $items = array(); + $path = $this->getPath($namespace); + if (is_null($path)) { + return $items; + } + $file = "{$path}/{$group}.php"; + if ($this->files->exists($file)) { + $items = $this->files->getRequire($file); + } + $file = "{$path}/{$environment}/{$group}.php"; + if ($this->files->exists($file)) { + $items = $this->mergeEnvironment($items, $file); + } + return $items; + } + protected function mergeEnvironment(array $items, $file) + { + return array_replace_recursive($items, $this->files->getRequire($file)); + } + public function exists($group, $namespace = null) + { + $key = $group . $namespace; + if (isset($this->exists[$key])) { + return $this->exists[$key]; + } + $path = $this->getPath($namespace); + if (is_null($path)) { + return $this->exists[$key] = false; + } + $file = "{$path}/{$group}.php"; + $exists = $this->files->exists($file); + return $this->exists[$key] = $exists; + } + public function cascadePackage($env, $package, $group, $items) + { + $file = "packages/{$package}/{$group}.php"; + if ($this->files->exists($path = $this->defaultPath . '/' . $file)) { + $items = array_merge($items, $this->getRequire($path)); + } + $path = $this->getPackagePath($env, $package, $group); + if ($this->files->exists($path)) { + $items = array_merge($items, $this->getRequire($path)); + } + return $items; + } + protected function getPackagePath($env, $package, $group) + { + $file = "packages/{$package}/{$env}/{$group}.php"; + return $this->defaultPath . '/' . $file; + } + protected function getPath($namespace) + { + if (is_null($namespace)) { + return $this->defaultPath; + } elseif (isset($this->hints[$namespace])) { + return $this->hints[$namespace]; + } + } + public function addNamespace($namespace, $hint) + { + $this->hints[$namespace] = $hint; + } + public function getNamespaces() + { + return $this->hints; + } + protected function getRequire($path) + { + return $this->files->getRequire($path); + } + public function getFilesystem() + { + return $this->files; + } +} +namespace Illuminate\Config; + +interface LoaderInterface +{ + public function load($environment, $group, $namespace = null); + public function exists($group, $namespace = null); + public function addNamespace($namespace, $hint); + public function getNamespaces(); + public function cascadePackage($environment, $package, $group, $items); +} +namespace Illuminate\Filesystem; + +use FilesystemIterator; +use Symfony\Component\Finder\Finder; +class FileNotFoundException extends \Exception +{ + +} +class Filesystem +{ + public function exists($path) + { + return file_exists($path); + } + public function get($path) + { + if ($this->isFile($path)) { + return file_get_contents($path); + } + throw new FileNotFoundException("File does not exist at path {$path}"); + } + public function getRemote($path) + { + return file_get_contents($path); + } + public function getRequire($path) + { + if ($this->isFile($path)) { + return require $path; + } + throw new FileNotFoundException("File does not exist at path {$path}"); + } + public function requireOnce($file) + { + require_once $file; + } + public function put($path, $contents) + { + return file_put_contents($path, $contents); + } + public function append($path, $data) + { + return file_put_contents($path, $data, FILE_APPEND); + } + public function delete($path) + { + return @unlink($path); + } + public function move($path, $target) + { + return rename($path, $target); + } + public function copy($path, $target) + { + return copy($path, $target); + } + public function extension($path) + { + return pathinfo($path, PATHINFO_EXTENSION); + } + public function type($path) + { + return filetype($path); + } + public function size($path) + { + return filesize($path); + } + public function lastModified($path) + { + return filemtime(realpath($path)); + } + public function isDirectory($directory) + { + return is_dir($directory); + } + public function isWritable($path) + { + return is_writable($path); + } + public function isFile($file) + { + return is_file($file); + } + public function glob($pattern, $flags = 0) + { + return glob($pattern, $flags); + } + public function files($directory) + { + $glob = glob($directory . '/*'); + if ($glob === false) { + return array(); + } + return array_filter($glob, function ($file) { + return filetype($file) == 'file'; + }); + } + public function allFiles($directory) + { + return iterator_to_array(Finder::create()->files()->in($directory), false); + } + public function directories($directory) + { + $directories = array(); + foreach (Finder::create()->in($directory)->directories()->depth(0) as $dir) { + $directories[] = $dir->getRealPath(); + } + return $directories; + } + public function makeDirectory($path, $mode = 511, $recursive = false) + { + return mkdir($path, $mode, $recursive); + } + public function copyDirectory($directory, $destination, $options = null) + { + if (!$this->isDirectory($directory)) { + return false; + } + $options = $options ?: FilesystemIterator::SKIP_DOTS; + if (!$this->isDirectory($destination)) { + $this->makeDirectory($destination, 511, true); + } + $items = new FilesystemIterator($directory, $options); + foreach ($items as $item) { + $target = $destination . '/' . $item->getBasename(); + if ($item->isDir()) { + $path = $item->getRealPath(); + if (!$this->copyDirectory($path, $target, $options)) { + return false; + } + } else { + if (!$this->copy($item->getRealPath(), $target)) { + return false; + } + } + } + return true; + } + public function deleteDirectory($directory, $preserve = false) + { + if (!$this->isDirectory($directory)) { + return; + } + $items = new FilesystemIterator($directory); + foreach ($items as $item) { + if ($item->isDir()) { + $this->deleteDirectory($item->getRealPath()); + } else { + $this->delete($item->getRealPath()); + } + } + if (!$preserve) { + @rmdir($directory); + } + } + public function cleanDirectory($directory) + { + return $this->deleteDirectory($directory, true); + } +} +namespace Illuminate\Foundation; + +class AliasLoader +{ + protected $aliases; + protected $registered = false; + protected static $instance; + public function __construct(array $aliases = array()) + { + $this->aliases = $aliases; + } + public static function getInstance(array $aliases = array()) + { + if (is_null(static::$instance)) { + static::$instance = new static($aliases); + } + $aliases = array_merge(static::$instance->getAliases(), $aliases); + static::$instance->setAliases($aliases); + return static::$instance; + } + public function load($alias) + { + if (isset($this->aliases[$alias])) { + return class_alias($this->aliases[$alias], $alias); + } + } + public function alias($class, $alias) + { + $this->aliases[$class] = $alias; + } + public function register() + { + if (!$this->registered) { + $this->prependToLoaderStack(); + $this->registered = true; + } + } + protected function prependToLoaderStack() + { + spl_autoload_register(array($this, 'load'), true, true); + } + public function getAliases() + { + return $this->aliases; + } + public function setAliases(array $aliases) + { + $this->aliases = $aliases; + } + public function isRegistered() + { + return $this->registered; + } + public function setRegistered($value) + { + $this->registered = $value; + } + public static function setInstance($loader) + { + static::$instance = $loader; + } +} +namespace Illuminate\Foundation; + +use Illuminate\Filesystem\Filesystem; +class ProviderRepository +{ + protected $files; + protected $manifestPath; + public function __construct(Filesystem $files, $manifestPath) + { + $this->files = $files; + $this->manifestPath = $manifestPath; + } + public function load(Application $app, array $providers) + { + $manifest = $this->loadManifest(); + if ($this->shouldRecompile($manifest, $providers)) { + $manifest = $this->compileManifest($app, $providers); + } + if ($app->runningInConsole()) { + $manifest['eager'] = $manifest['providers']; + } + foreach ($manifest['eager'] as $provider) { + $app->register($this->createProvider($app, $provider)); + } + $app->setDeferredServices($manifest['deferred']); + } + protected function compileManifest(Application $app, $providers) + { + $manifest = $this->freshManifest($providers); + foreach ($providers as $provider) { + $instance = $this->createProvider($app, $provider); + if ($instance->isDeferred()) { + foreach ($instance->provides() as $service) { + $manifest['deferred'][$service] = $provider; + } + } else { + $manifest['eager'][] = $provider; + } + } + return $this->writeManifest($manifest); + } + public function createProvider(Application $app, $provider) + { + return new $provider($app); + } + public function shouldRecompile($manifest, $providers) + { + return is_null($manifest) or $manifest['providers'] != $providers; + } + public function loadManifest() + { + $path = $this->manifestPath . '/services.json'; + if ($this->files->exists($path)) { + return json_decode($this->files->get($path), true); + } + } + public function writeManifest($manifest) + { + $path = $this->manifestPath . '/services.json'; + $this->files->put($path, json_encode($manifest)); + return $manifest; + } + protected function getManifestPath($app) + { + return $this->manifestPath; + } + protected function freshManifest(array $providers) + { + list($eager, $deferred) = array(array(), array()); + return compact('providers', 'eager', 'deferred'); + } + public function getFilesystem() + { + return $this->files; + } +} +namespace Illuminate\Cookie; + +use Illuminate\Support\ServiceProvider; +class CookieServiceProvider extends ServiceProvider +{ + public function register() + { + $this->app['cookie'] = $this->app->share(function ($app) { + $cookies = new CookieJar($app['request'], $app['encrypter']); + $config = $app['config']['session']; + return $cookies->setDefaultPathAndDomain($config['path'], $config['domain']); + }); + } +} +namespace Illuminate\Database; + +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\ServiceProvider; +use Illuminate\Database\Connectors\ConnectionFactory; +class DatabaseServiceProvider extends ServiceProvider +{ + public function boot() + { + Model::setConnectionResolver($this->app['db']); + Model::setEventDispatcher($this->app['events']); + } + public function register() + { + $this->app['db.factory'] = $this->app->share(function ($app) { + return new ConnectionFactory($app); + }); + $this->app['db'] = $this->app->share(function ($app) { + return new DatabaseManager($app, $app['db.factory']); + }); + } +} +namespace Illuminate\Encryption; + +use Illuminate\Support\ServiceProvider; +class EncryptionServiceProvider extends ServiceProvider +{ + public function register() + { + $this->app['encrypter'] = $this->app->share(function ($app) { + return new Encrypter($app['config']['app.key']); + }); + } +} +namespace Illuminate\Filesystem; + +use Illuminate\Support\ServiceProvider; +class FilesystemServiceProvider extends ServiceProvider +{ + public function register() + { + $this->app['files'] = $this->app->share(function () { + return new Filesystem(); + }); + } +} +namespace Illuminate\Session; + +use Illuminate\Support\ServiceProvider; +class SessionServiceProvider extends ServiceProvider +{ + public function boot() + { + $this->registerSessionEvents(); + } + public function register() + { + $this->setupDefaultDriver(); + $this->registerSessionManager(); + $this->registerSessionDriver(); + } + protected function setupDefaultDriver() + { + if ($this->app->runningInConsole()) { + $this->app['config']['session.driver'] = 'array'; + } + } + protected function registerSessionManager() + { + $this->app['session.manager'] = $this->app->share(function ($app) { + return new SessionManager($app); + }); + } + protected function registerSessionDriver() + { + $this->app['session'] = $this->app->share(function ($app) { + $manager = $app['session.manager']; + return $manager->driver(); + }); + } + protected function registerSessionEvents() + { + $app = $this->app; + $config = $app['config']['session']; + if (!is_null($config['driver'])) { + $this->registerBootingEvent(); + $this->registerCloseEvent(); + } + } + protected function registerBootingEvent() + { + $app = $this->app; + $this->app->booting(function ($app) use($app) { + $app['session']->start(); + }); + } + protected function registerCloseEvent() + { + if ($this->getDriver() == 'array') { + return; + } + $this->registerCookieToucher(); + $app = $this->app; + $this->app->close(function () use($app) { + $app['session']->save(); + }); + } + protected function registerCookieToucher() + { + $me = $this; + $this->app->close(function () use($me) { + if (!headers_sent()) { + $me->touchSessionCookie(); + } + }); + } + public function touchSessionCookie() + { + $config = $this->app['config']['session']; + $expire = $this->getExpireTime($config); + setcookie($config['cookie'], session_id(), $expire, $config['path'], $config['domain']); + } + protected function getExpireTime($config) + { + return $config['lifetime'] == 0 ? 0 : time() + $config['lifetime'] * 60; + } + protected function getDriver() + { + return $this->app['config']['session.driver']; + } +} +namespace Illuminate\View; + +use Illuminate\Support\MessageBag; +use Illuminate\View\Engines\PhpEngine; +use Illuminate\Support\ServiceProvider; +use Illuminate\View\Engines\BladeEngine; +use Illuminate\View\Engines\CompilerEngine; +use Illuminate\View\Engines\EngineResolver; +use Illuminate\View\Compilers\BladeCompiler; +class ViewServiceProvider extends ServiceProvider +{ + public function register() + { + $this->registerEngineResolver(); + $this->registerViewFinder(); + $this->registerEnvironment(); + $this->registerSessionBinder(); + } + public function registerEngineResolver() + { + list($me, $app) = array($this, $this->app); + $app['view.engine.resolver'] = $app->share(function ($app) use($me) { + $resolver = new EngineResolver(); + foreach (array('php', 'blade') as $engine) { + $me->{'register' . ucfirst($engine) . 'Engine'}($resolver); + } + return $resolver; + }); + } + public function registerPhpEngine($resolver) + { + $resolver->register('php', function () { + return new PhpEngine(); + }); + } + public function registerBladeEngine($resolver) + { + $app = $this->app; + $resolver->register('blade', function () use($app) { + $cache = $app['path.storage'] . '/views'; + $compiler = new BladeCompiler($app['files'], $cache); + return new CompilerEngine($compiler, $app['files']); + }); + } + public function registerViewFinder() + { + $this->app['view.finder'] = $this->app->share(function ($app) { + $paths = $app['config']['view.paths']; + return new FileViewFinder($app['files'], $paths); + }); + } + public function registerEnvironment() + { + $this->app['view'] = $this->app->share(function ($app) { + $resolver = $app['view.engine.resolver']; + $finder = $app['view.finder']; + $env = new Environment($resolver, $finder, $app['events']); + $env->setContainer($app); + $env->share('app', $app); + return $env; + }); + } + protected function registerSessionBinder() + { + list($app, $me) = array($this->app, $this); + $app->booted(function () use($app, $me) { + if ($me->sessionHasErrors($app)) { + $errors = $app['session']->get('errors'); + $app['view']->share('errors', $errors); + } else { + $app['view']->share('errors', new MessageBag()); + } + }); + } + public function sessionHasErrors($app) + { + $config = $app['config']['session']; + if (isset($app['session']) and !is_null($config['driver'])) { + return $app['session']->has('errors'); + } + } +} +namespace Illuminate\Routing; + +use Closure; +use Illuminate\Http\Response; +use Illuminate\Container\Container; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\RequestContext; +use Illuminate\Routing\Controllers\Inspector; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\Exception\ExceptionInterface; +use Symfony\Component\HttpFoundation\Response as SymfonyResponse; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +class Router +{ + protected $routes; + protected $filters = array(); + protected $patternFilters = array(); + protected $globalFilters = array(); + protected $groupStack = array(); + protected $container; + protected $inspector; + protected $patterns = array(); + protected $binders = array(); + protected $currentRequest; + protected $currentRoute; + protected $runFilters = true; + protected $resourceDefaults = array('index', 'create', 'store', 'show', 'edit', 'update', 'destroy'); + public function __construct(Container $container = null) + { + $this->container = $container; + $this->routes = new RouteCollection(); + $this->bind('_missing', function ($v) { + return explode('/', $v); + }); + } + public function get($pattern, $action) + { + return $this->createRoute('get', $pattern, $action); + } + public function post($pattern, $action) + { + return $this->createRoute('post', $pattern, $action); + } + public function put($pattern, $action) + { + return $this->createRoute('put', $pattern, $action); + } + public function patch($pattern, $action) + { + return $this->createRoute('patch', $pattern, $action); + } + public function delete($pattern, $action) + { + return $this->createRoute('delete', $pattern, $action); + } + public function options($pattern, $action) + { + return $this->createRoute('options', $pattern, $action); + } + public function match($method, $pattern, $action) + { + return $this->createRoute($method, $pattern, $action); + } + public function any($pattern, $action) + { + return $this->createRoute('get|post|put|patch|delete', $pattern, $action); + } + public function controllers(array $controllers) + { + foreach ($controllers as $uri => $name) { + $this->controller($uri, $name); + } + } + public function controller($uri, $controller, $names = array()) + { + $routable = $this->getInspector()->getRoutable($controller, $uri); + foreach ($routable as $method => $routes) { + foreach ($routes as $route) { + $this->registerInspected($route, $controller, $method, $names); + } + } + $this->addFallthroughRoute($controller, $uri); + } + protected function registerInspected($route, $controller, $method, &$names) + { + $action = array('uses' => $controller . '@' . $method); + $action['as'] = array_pull($names, $method); + $this->{$route['verb']}($route['uri'], $action); + } + protected function addFallthroughRoute($controller, $uri) + { + $missing = $this->any($uri . '/{_missing}', $controller . '@missingMethod'); + $missing->where('_missing', '(.*)'); + } + public function resource($resource, $controller, array $options = array()) + { + if (str_contains($resource, '/')) { + $this->prefixedResource($resource, $controller, $options); + return; + } + $base = $this->getBaseResource($resource); + $defaults = $this->resourceDefaults; + foreach ($this->getResourceMethods($defaults, $options) as $method) { + $this->{'addResource' . ucfirst($method)}($resource, $base, $controller); + } + } + protected function prefixedResource($resource, $controller, array $options) + { + list($resource, $prefix) = $this->extractResourcePrefix($resource); + $me = $this; + return $this->group(array('prefix' => $prefix), function () use($me, $resource, $controller, $options) { + $me->resource($resource, $controller, $options); + }); + } + protected function extractResourcePrefix($resource) + { + $segments = explode('/', $resource); + return array($segments[count($segments) - 1], implode('/', array_slice($segments, 0, -1))); + } + protected function getResourceMethods($defaults, $options) + { + if (isset($options['only'])) { + return array_intersect($defaults, $options['only']); + } elseif (isset($options['except'])) { + return array_diff($defaults, $options['except']); + } + return $defaults; + } + protected function addResourceIndex($name, $base, $controller) + { + $action = $this->getResourceAction($name, $controller, 'index'); + return $this->get($this->getResourceUri($name), $action); + } + protected function addResourceCreate($name, $base, $controller) + { + $action = $this->getResourceAction($name, $controller, 'create'); + return $this->get($this->getResourceUri($name) . '/create', $action); + } + protected function addResourceStore($name, $base, $controller) + { + $action = $this->getResourceAction($name, $controller, 'store'); + return $this->post($this->getResourceUri($name), $action); + } + protected function addResourceShow($name, $base, $controller) + { + $uri = $this->getResourceUri($name) . '/{' . $base . '}'; + return $this->get($uri, $this->getResourceAction($name, $controller, 'show')); + } + protected function addResourceEdit($name, $base, $controller) + { + $uri = $this->getResourceUri($name) . '/{' . $base . '}/edit'; + return $this->get($uri, $this->getResourceAction($name, $controller, 'edit')); + } + protected function addResourceUpdate($name, $base, $controller) + { + $this->addPutResourceUpdate($name, $base, $controller); + return $this->addPatchResourceUpdate($name, $base, $controller); + } + protected function addPutResourceUpdate($name, $base, $controller) + { + $uri = $this->getResourceUri($name) . '/{' . $base . '}'; + return $this->put($uri, $this->getResourceAction($name, $controller, 'update')); + } + protected function addPatchResourceUpdate($name, $base, $controller) + { + $uri = $this->getResourceUri($name) . '/{' . $base . '}'; + $this->patch($uri, $controller . '@update'); + } + protected function addResourceDestroy($name, $base, $controller) + { + $uri = $this->getResourceUri($name) . '/{' . $base . '}'; + return $this->delete($uri, $this->getResourceAction($name, $controller, 'destroy')); + } + public function getResourceUri($resource) + { + if (!str_contains($resource, '.')) { + return $resource; + } + $segments = explode('.', $resource); + $nested = $this->getNestedResourceUri($segments); + $last = $this->getResourceWildcard(last($segments)); + return str_replace('/{' . $last . '}', '', $nested); + } + protected function getNestedResourceUri(array $segments) + { + $me = $this; + return implode('/', array_map(function ($s) use($me) { + return $s . '/{' . $me->getResourceWildcard($s) . '}'; + }, $segments)); + } + protected function getResourceAction($resource, $controller, $method) + { + $name = $resource . '.' . $method; + $name = $this->getResourceName($resource, $method); + return array('as' => $name, 'uses' => $controller . '@' . $method); + } + protected function getResourceName($resource, $method) + { + if (count($this->groupStack) == 0) { + return $resource . '.' . $method; + } + return $this->getResourcePrefix($resource, $method); + } + protected function getResourcePrefix($resource, $method) + { + $prefix = str_replace('/', '.', $this->getGroupPrefix()); + if ($prefix != '') { + $prefix .= '.'; + } + return "{$prefix}{$resource}.{$method}"; + } + protected function getBaseResource($resource) + { + $segments = explode('.', $resource); + return $this->getResourceWildcard($segments[count($segments) - 1]); + } + public function getResourceWildcard($value) + { + return str_replace('-', '_', $value); + } + public function group(array $attributes, Closure $callback) + { + $this->updateGroupStack($attributes); + call_user_func($callback); + array_pop($this->groupStack); + } + protected function updateGroupStack(array $attributes) + { + if (count($this->groupStack) > 0) { + $last = $this->groupStack[count($this->groupStack) - 1]; + $this->groupStack[] = array_merge_recursive($last, $attributes); + } else { + $this->groupStack[] = $attributes; + } + } + protected function createRoute($method, $pattern, $action) + { + if (!is_array($action)) { + $action = $this->parseAction($action); + } + $groupCount = count($this->groupStack); + if ($groupCount > 0) { + $index = $groupCount - 1; + $action = $this->mergeGroup($action, $index); + } + list($pattern, $optional) = $this->getOptional($pattern); + if (isset($action['prefix'])) { + $prefix = $action['prefix']; + $pattern = $this->addPrefix($pattern, $prefix); + } + $route = with(new Route($pattern))->setOptions(array('_call' => $this->getCallback($action)))->setRouter($this)->addRequirements($this->patterns); + $route->setRequirement('_method', $method); + $this->setAttributes($route, $action, $optional); + $name = $this->getName($method, $pattern, $action); + $this->routes->add($name, $route); + return $route; + } + protected function parseAction($action) + { + if ($action instanceof Closure) { + return array($action); + } elseif (is_string($action)) { + return array('uses' => $action); + } + throw new \InvalidArgumentException('Unroutable action.'); + } + protected function mergeGroup($action, $index) + { + $prefix = $this->mergeGroupPrefix($action); + $action = array_merge_recursive($this->groupStack[$index], $action); + if ($prefix != '') { + $action['prefix'] = $prefix; + } + return $action; + } + protected function getGroupPrefix() + { + if (count($this->groupStack) > 0) { + $group = $this->groupStack[count($this->groupStack) - 1]; + if (isset($group['prefix'])) { + if (is_array($group['prefix'])) { + return implode('/', $group['prefix']); + } + return $group['prefix']; + } + } + return ''; + } + protected function mergeGroupPrefix($action) + { + $prefix = isset($action['prefix']) ? $action['prefix'] : ''; + return trim($this->getGroupPrefix() . '/' . $prefix, '/'); + } + protected function addPrefix($pattern, $prefix) + { + $pattern = trim($prefix, '/') . '/' . ltrim($pattern, '/'); + return trim($pattern, '/'); + } + protected function setAttributes(Route $route, $action, $optional) + { + if (in_array('https', $action)) { + $route->setRequirement('_scheme', 'https'); + } + if (in_array('http', $action)) { + $route->setRequirement('_scheme', 'http'); + } + if (isset($action['before'])) { + $route->setBeforeFilters($action['before']); + } + if (isset($action['after'])) { + $route->setAfterFilters($action['after']); + } + if (isset($action['uses'])) { + $route->setOption('_uses', $action['uses']); + } + if (isset($action['domain'])) { + $route->setHost($action['domain']); + } + foreach ($optional as $key) { + $route->setDefault($key, null); + } + } + protected function getOptional($pattern) + { + $optional = array(); + preg_match_all('#\\{(\\w+)\\?\\}#', $pattern, $matches); + foreach ($matches[0] as $key => $value) { + $optional[] = $name = $matches[1][$key]; + $pattern = str_replace($value, '{' . $name . '}', $pattern); + } + return array($pattern, $optional); + } + protected function getName($method, $pattern, array $action) + { + if (isset($action['as'])) { + return $action['as']; + } + $domain = isset($action['domain']) ? $action['domain'] . ' ' : ''; + return "{$domain}{$method} {$pattern}"; + } + protected function getCallback(array $action) + { + foreach ($action as $key => $attribute) { + if ($key === 'uses') { + return $this->createControllerCallback($attribute); + } elseif ($attribute instanceof Closure) { + return $attribute; + } + } + } + protected function createControllerCallback($attribute) + { + $ioc = $this->container; + $me = $this; + return function () use($me, $ioc, $attribute) { + list($controller, $method) = explode('@', $attribute); + $route = $me->getCurrentRoute(); + $args = array_values($route->getParametersWithoutDefaults()); + $instance = $ioc->make($controller); + return $instance->callAction($ioc, $me, $method, $args); + }; + } + public function dispatch(Request $request) + { + $this->currentRequest = $request; + $response = $this->callGlobalFilter($request, 'before'); + if (!is_null($response)) { + $response = $this->prepare($response, $request); + } else { + $this->currentRoute = $route = $this->findRoute($request); + $response = $route->run($request); + } + $this->callAfterFilter($request, $response); + return $response; + } + protected function findRoute(Request $request) + { + try { + $path = $request->getPathInfo(); + $parameters = $this->getUrlMatcher($request)->match($path); + } catch (ExceptionInterface $e) { + $this->handleRoutingException($e); + } + $route = $this->routes->get($parameters['_route']); + $route->setParameters($parameters); + return $route; + } + public function before($callback) + { + $this->globalFilters['before'][] = $this->buildGlobalFilter($callback); + } + public function after($callback) + { + $this->globalFilters['after'][] = $this->buildGlobalFilter($callback); + } + public function close($callback) + { + $this->globalFilters['close'][] = $this->buildGlobalFilter($callback); + } + public function finish($callback) + { + $this->globalFilters['finish'][] = $this->buildGlobalFilter($callback); + } + protected function buildGlobalFilter($callback) + { + if (is_string($callback)) { + $container = $this->container; + return function () use($callback, $container) { + $callable = array($container->make($callback), 'filter'); + return call_user_func_array($callable, func_get_args()); + }; + } else { + return $callback; + } + } + public function filter($name, $callback) + { + $this->filters[$name] = $callback; + } + public function getFilter($name) + { + if (array_key_exists($name, $this->filters)) { + $filter = $this->filters[$name]; + if (is_string($filter)) { + return $this->getClassBasedFilter($filter); + } + return $filter; + } + } + protected function getClassBasedFilter($filter) + { + if (str_contains($filter, '@')) { + list($class, $method) = explode('@', $filter); + return array($this->container->make($class), $method); + } + return array($this->container->make($filter), 'filter'); + } + public function when($pattern, $names, $methods = null) + { + foreach ((array) $names as $name) { + if (!is_null($methods)) { + $methods = array_change_key_case((array) $methods); + } + $this->patternFilters[$pattern][] = compact('name', 'methods'); + } + } + public function findPatternFilters(Request $request) + { + $results = array(); + foreach ($this->patternFilters as $pattern => $filters) { + if (str_is('/' . $pattern, $request->getPathInfo())) { + $merge = $this->filterPatternsByMethod($request, $filters); + $results = array_merge($results, $merge); + } + } + return $results; + } + protected function filterPatternsByMethod(Request $request, $filters) + { + $results = array(); + $method = strtolower($request->getMethod()); + foreach ($filters as $filter) { + if (is_null($filter['methods']) or in_array($method, $filter['methods'])) { + $results[] = $filter['name']; + } + } + return $results; + } + protected function callAfterFilter(Request $request, SymfonyResponse $response) + { + $this->callGlobalFilter($request, 'after', array($response)); + } + public function callFinishFilter(Request $request, SymfonyResponse $response) + { + $this->callGlobalFilter($request, 'finish', array($response)); + } + public function callCloseFilter(Request $request, SymfonyResponse $response) + { + $this->callGlobalFilter($request, 'close', array($response)); + } + protected function callGlobalFilter(Request $request, $name, array $parameters = array()) + { + if (!$this->filtersEnabled()) { + return; + } + array_unshift($parameters, $request); + if (isset($this->globalFilters[$name])) { + foreach ($this->globalFilters[$name] as $filter) { + $response = call_user_func_array($filter, $parameters); + if (!is_null($response)) { + return $response; + } + } + } + } + public function pattern($key, $pattern) + { + $this->patterns[$key] = $pattern; + } + public function model($key, $class, Closure $callback = null) + { + return $this->bind($key, function ($value) use($class, $callback) { + if (is_null($value)) { + return null; + } + if (!is_null($model = with(new $class())->find($value))) { + return $model; + } + if ($callback instanceof Closure) { + return call_user_func($callback); + } + throw new NotFoundHttpException(); + }); + } + public function bind($key, $binder) + { + $this->binders[str_replace('-', '_', $key)] = $binder; + } + public function hasBinder($key) + { + return isset($this->binders[$key]); + } + public function performBinding($key, $value, $route) + { + return call_user_func($this->binders[$key], $value, $route); + } + public function prepare($value, Request $request) + { + if (!$value instanceof SymfonyResponse) { + $value = new Response($value); + } + return $value->prepare($request); + } + protected function handleRoutingException(\Exception $e) + { + if ($e instanceof ResourceNotFoundException) { + throw new NotFoundHttpException($e->getMessage()); + } elseif ($e instanceof MethodNotAllowedException) { + $allowed = $e->getAllowedMethods(); + throw new MethodNotAllowedHttpException($allowed, $e->getMessage()); + } + } + public function currentRouteName() + { + foreach ($this->routes->all() as $name => $route) { + if ($route === $this->currentRoute) { + return $name; + } + } + } + public function currentRouteNamed($name) + { + $route = $this->routes->get($name); + return !is_null($route) and $route === $this->currentRoute; + } + public function currentRouteAction() + { + $currentRoute = $this->currentRoute; + if (!is_null($currentRoute)) { + return $currentRoute->getOption('_uses'); + } + } + public function currentRouteUses($action) + { + return $this->currentRouteAction() === $action; + } + public function filtersEnabled() + { + return $this->runFilters; + } + public function enableFilters() + { + $this->runFilters = true; + } + public function disableFilters() + { + $this->runFilters = false; + } + public function getRoutes() + { + return $this->routes; + } + public function getRequest() + { + return $this->currentRequest; + } + public function getCurrentRoute() + { + return $this->currentRoute; + } + public function setCurrentRoute(Route $route) + { + $this->currentRoute = $route; + } + public function getFilters() + { + return $this->filters; + } + public function getGlobalFilters() + { + return $this->globalFilters; + } + protected function getUrlMatcher(Request $request) + { + $context = new RequestContext(); + $context->fromRequest($request); + return new UrlMatcher($this->routes, $context); + } + public function getInspector() + { + return $this->inspector ?: new Controllers\Inspector(); + } + public function setInspector(Inspector $inspector) + { + $this->inspector = $inspector; + } + public function getContainer() + { + return $this->container; + } + public function setContainer(Container $container) + { + $this->container = $container; + } +} +namespace Symfony\Component\Routing; + +use Symfony\Component\Config\Resource\ResourceInterface; +class RouteCollection implements \IteratorAggregate, \Countable +{ + private $routes = array(); + private $resources = array(); + public function __clone() + { + foreach ($this->routes as $name => $route) { + $this->routes[$name] = clone $route; + } + } + public function getIterator() + { + return new \ArrayIterator($this->routes); + } + public function count() + { + return count($this->routes); + } + public function add($name, Route $route) + { + unset($this->routes[$name]); + $this->routes[$name] = $route; + } + public function all() + { + return $this->routes; + } + public function get($name) + { + return isset($this->routes[$name]) ? $this->routes[$name] : null; + } + public function remove($name) + { + foreach ((array) $name as $n) { + unset($this->routes[$n]); + } + } + public function addCollection(RouteCollection $collection) + { + foreach ($collection->all() as $name => $route) { + unset($this->routes[$name]); + $this->routes[$name] = $route; + } + $this->resources = array_merge($this->resources, $collection->getResources()); + } + public function addPrefix($prefix, array $defaults = array(), array $requirements = array()) + { + $prefix = trim(trim($prefix), '/'); + if ('' === $prefix) { + return; + } + foreach ($this->routes as $route) { + $route->setPath('/' . $prefix . $route->getPath()); + $route->addDefaults($defaults); + $route->addRequirements($requirements); + } + } + public function setHost($pattern, array $defaults = array(), array $requirements = array()) + { + foreach ($this->routes as $route) { + $route->setHost($pattern); + $route->addDefaults($defaults); + $route->addRequirements($requirements); + } + } + public function addDefaults(array $defaults) + { + if ($defaults) { + foreach ($this->routes as $route) { + $route->addDefaults($defaults); + } + } + } + public function addRequirements(array $requirements) + { + if ($requirements) { + foreach ($this->routes as $route) { + $route->addRequirements($requirements); + } + } + } + public function addOptions(array $options) + { + if ($options) { + foreach ($this->routes as $route) { + $route->addOptions($options); + } + } + } + public function setSchemes($schemes) + { + foreach ($this->routes as $route) { + $route->setSchemes($schemes); + } + } + public function setMethods($methods) + { + foreach ($this->routes as $route) { + $route->setMethods($methods); + } + } + public function getResources() + { + return array_unique($this->resources); + } + public function addResource(ResourceInterface $resource) + { + $this->resources[] = $resource; + } +} +namespace Illuminate\Workbench; + +use Illuminate\Support\ServiceProvider; +use Illuminate\Workbench\Console\WorkbenchMakeCommand; +class WorkbenchServiceProvider extends ServiceProvider +{ + protected $defer = false; + public function register() + { + $this->app['package.creator'] = $this->app->share(function ($app) { + return new PackageCreator($app['files']); + }); + $this->app['command.workbench'] = $this->app->share(function ($app) { + return new WorkbenchMakeCommand($app['package.creator']); + }); + $this->commands('command.workbench'); + } + public function provides() + { + return array('package.creator', 'command.workbench'); + } +} +namespace Illuminate\Events; + +use Illuminate\Container\Container; +class Dispatcher +{ + protected $container; + protected $listeners = array(); + protected $wildcards = array(); + protected $sorted = array(); + public function __construct(Container $container = null) + { + $this->container = $container; + } + public function listen($event, $listener, $priority = 0) + { + if (str_contains($event, '*')) { + return $this->setupWildcardListen($event, $listener, $priority = 0); + } + $this->listeners[$event][$priority][] = $this->makeListener($listener); + unset($this->sorted[$event]); + } + protected function setupWildcardListen($event, $listener, $priority) + { + $this->wildcards[$event][] = $listener; + } + public function hasListeners($eventName) + { + return isset($this->listeners[$eventName]); + } + public function queue($event, $payload = array()) + { + $me = $this; + $this->listen($event . '_queue', function () use($me, $event, $payload) { + $me->fire($event, $payload); + }); + } + public function subscribe($subscriber) + { + $subscriber = $this->resolveSubscriber($subscriber); + $subscriber->subscribe($this); + } + protected function resolveSubscriber($subscriber) + { + if (is_string($subscriber)) { + return $this->container->make($subscriber); + } + return $subscriber; + } + public function until($event, $payload = array()) + { + return $this->fire($event, $payload, true); + } + public function flush($event) + { + $this->fire($event . '_queue'); + } + public function fire($event, $payload = array(), $halt = false) + { + $responses = array(); + if (!is_array($payload)) { + $payload = array($payload); + } + $payload[] = $event; + foreach ($this->getListeners($event) as $listener) { + $response = call_user_func_array($listener, $payload); + if (!is_null($response) and $halt) { + return $response; + } + if ($response === false) { + break; + } + $responses[] = $response; + } + return $halt ? null : $responses; + } + public function getListeners($eventName) + { + $wildcards = $this->getWildcardListeners($eventName); + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + return array_merge($this->sorted[$eventName], $wildcards); + } + protected function getWildcardListeners($eventName) + { + $wildcards = array(); + foreach ($this->wildcards as $key => $listeners) { + if (str_is($key, $eventName)) { + $wildcards = array_merge($wildcards, $listeners); + } + } + return $wildcards; + } + protected function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } + } + public function makeListener($listener) + { + if (is_string($listener)) { + $listener = $this->createClassListener($listener); + } + return $listener; + } + public function createClassListener($listener) + { + $container = $this->container; + return function () use($listener, $container) { + $segments = explode('@', $listener); + $method = count($segments) == 2 ? $segments[1] : 'handle'; + $callable = array($container->make($segments[0]), $method); + $data = func_get_args(); + return call_user_func_array($callable, $data); + }; + } + public function forget($event) + { + unset($this->listeners[$event]); + unset($this->sorted[$event]); + } +} +namespace Illuminate\Database\Eloquent; + +use Closure; +use DateTime; +use Carbon\Carbon; +use ArrayAccess; +use Illuminate\Events\Dispatcher; +use Illuminate\Database\Connection; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Contracts\JsonableInterface; +use Illuminate\Support\Contracts\ArrayableInterface; +use Illuminate\Database\Eloquent\Relations\MorphOne; +use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Query\Builder as QueryBuilder; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\ConnectionResolverInterface as Resolver; +abstract class Model implements ArrayAccess, ArrayableInterface, JsonableInterface +{ + protected $connection; + protected $table; + protected $primaryKey = 'id'; + protected $perPage = 15; + public $incrementing = true; + public $timestamps = true; + protected $attributes = array(); + protected $original = array(); + protected $relations = array(); + protected $hidden = array(); + protected $visible = array(); + protected $fillable = array(); + protected $guarded = array('*'); + protected $touches = array(); + protected $with = array(); + public $exists = false; + protected $softDelete = false; + public static $snakeAttributes = true; + protected static $resolver; + protected static $dispatcher; + protected static $booted = array(); + protected static $unguarded = false; + protected static $mutatorCache = array(); + const CREATED_AT = 'created_at'; + const UPDATED_AT = 'updated_at'; + const DELETED_AT = 'deleted_at'; + public function __construct(array $attributes = array()) + { + if (!isset(static::$booted[get_class($this)])) { + static::boot(); + static::$booted[get_class($this)] = true; + } + $this->fill($attributes); + } + protected static function boot() + { + $class = get_called_class(); + static::$mutatorCache[$class] = array(); + foreach (get_class_methods($class) as $method) { + if (preg_match('/^get(.+)Attribute$/', $method, $matches)) { + if (static::$snakeAttributes) { + $matches[1] = snake_case($matches[1]); + } + static::$mutatorCache[$class][] = lcfirst($matches[1]); + } + } + } + public static function observe($class) + { + $instance = new static(); + $className = get_class($class); + foreach ($instance->getObservableEvents() as $event) { + if (method_exists($class, $event)) { + static::registerModelEvent($event, $className . '@' . $event); + } + } + } + public function fill(array $attributes) + { + foreach ($attributes as $key => $value) { + $key = $this->removeTableFromKey($key); + if ($this->isFillable($key)) { + $this->setAttribute($key, $value); + } elseif ($this->totallyGuarded()) { + throw new MassAssignmentException($key); + } + } + return $this; + } + public function newInstance($attributes = array(), $exists = false) + { + $model = new static((array) $attributes); + $model->exists = $exists; + return $model; + } + public function newFromBuilder($attributes = array()) + { + $instance = $this->newInstance(array(), true); + $instance->setRawAttributes((array) $attributes, true); + return $instance; + } + public static function create(array $attributes) + { + $model = new static($attributes); + $model->save(); + return $model; + } + public static function on($connection = null) + { + $instance = new static(); + $instance->setConnection($connection); + return $instance->newQuery(); + } + public static function all($columns = array('*')) + { + $instance = new static(); + return $instance->newQuery()->get($columns); + } + public static function find($id, $columns = array('*')) + { + $instance = new static(); + if (is_array($id)) { + return $instance->newQuery()->whereIn($instance->getKeyName(), $id)->get($columns); + } + return $instance->newQuery()->find($id, $columns); + } + public static function findOrFail($id, $columns = array('*')) + { + if (!is_null($model = static::find($id, $columns))) { + return $model; + } + throw new ModelNotFoundException(); + } + public function load($relations) + { + if (is_string($relations)) { + $relations = func_get_args(); + } + $query = $this->newQuery()->with($relations); + $query->eagerLoadRelations(array($this)); + } + public static function with($relations) + { + if (is_string($relations)) { + $relations = func_get_args(); + } + $instance = new static(); + return $instance->newQuery()->with($relations); + } + public function hasOne($related, $foreignKey = null) + { + $foreignKey = $foreignKey ?: $this->getForeignKey(); + $instance = new $related(); + return new HasOne($instance->newQuery(), $this, $instance->getTable() . '.' . $foreignKey); + } + public function morphOne($related, $name, $type = null, $id = null) + { + $instance = new $related(); + list($type, $id) = $this->getMorphs($name, $type, $id); + $table = $instance->getTable(); + return new MorphOne($instance->newQuery(), $this, $table . '.' . $type, $table . '.' . $id); + } + public function belongsTo($related, $foreignKey = null) + { + list(, $caller) = debug_backtrace(false); + $relation = $caller['function']; + if (is_null($foreignKey)) { + $foreignKey = snake_case($relation) . '_id'; + } + $instance = new $related(); + $query = $instance->newQuery(); + return new BelongsTo($query, $this, $foreignKey, $relation); + } + public function morphTo($name = null, $type = null, $id = null) + { + if (is_null($name)) { + list(, $caller) = debug_backtrace(false); + $name = snake_case($caller['function']); + } + list($type, $id) = $this->getMorphs($name, $type, $id); + $class = $this->{$type}; + return $this->belongsTo($class, $id); + } + public function hasMany($related, $foreignKey = null) + { + $foreignKey = $foreignKey ?: $this->getForeignKey(); + $instance = new $related(); + return new HasMany($instance->newQuery(), $this, $instance->getTable() . '.' . $foreignKey); + } + public function morphMany($related, $name, $type = null, $id = null) + { + $instance = new $related(); + list($type, $id) = $this->getMorphs($name, $type, $id); + $table = $instance->getTable(); + return new MorphMany($instance->newQuery(), $this, $table . '.' . $type, $table . '.' . $id); + } + public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null) + { + $caller = $this->getBelongsToManyCaller(); + $foreignKey = $foreignKey ?: $this->getForeignKey(); + $instance = new $related(); + $otherKey = $otherKey ?: $instance->getForeignKey(); + if (is_null($table)) { + $table = $this->joiningTable($related); + } + $query = $instance->newQuery(); + return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $caller['function']); + } + protected function getBelongsToManyCaller() + { + $self = __FUNCTION__; + return array_first(debug_backtrace(false), function ($trace) use($self) { + $caller = $trace['function']; + return $caller != 'belongsToMany' and $caller != $self; + }); + } + public function joiningTable($related) + { + $base = snake_case(class_basename($this)); + $related = snake_case(class_basename($related)); + $models = array($related, $base); + sort($models); + return strtolower(implode('_', $models)); + } + public static function destroy($ids) + { + $ids = is_array($ids) ? $ids : func_get_args(); + $instance = new static(); + $key = $instance->getKeyName(); + foreach ($instance->whereIn($key, $ids)->get() as $model) { + $model->delete(); + } + } + public function delete() + { + if ($this->exists) { + if ($this->fireModelEvent('deleting') === false) { + return false; + } + $this->touchOwners(); + $this->performDeleteOnModel(); + $this->exists = false; + $this->fireModelEvent('deleted', false); + return true; + } + } + public function forceDelete() + { + $softDelete = $this->softDelete; + $this->softDelete = false; + $this->delete(); + $this->softDelete = $softDelete; + } + protected function performDeleteOnModel() + { + $query = $this->newQuery()->where($this->getKeyName(), $this->getKey()); + if ($this->softDelete) { + $query->update(array(static::DELETED_AT => new DateTime())); + } else { + $query->delete(); + } + } + public function restore() + { + if ($this->softDelete) { + $this->{static::DELETED_AT} = null; + return $this->save(); + } + } + public static function saving($callback) + { + static::registerModelEvent('saving', $callback); + } + public static function saved($callback) + { + static::registerModelEvent('saved', $callback); + } + public static function updating($callback) + { + static::registerModelEvent('updating', $callback); + } + public static function updated($callback) + { + static::registerModelEvent('updated', $callback); + } + public static function creating($callback) + { + static::registerModelEvent('creating', $callback); + } + public static function created($callback) + { + static::registerModelEvent('created', $callback); + } + public static function deleting($callback) + { + static::registerModelEvent('deleting', $callback); + } + public static function deleted($callback) + { + static::registerModelEvent('deleted', $callback); + } + public static function flushEventListeners() + { + if (!isset(static::$dispatcher)) { + return; + } + $instance = new static(); + foreach ($instance->getObservableEvents() as $event) { + static::$dispatcher->forget("eloquent.{$event}: " . get_called_class()); + } + } + protected static function registerModelEvent($event, $callback) + { + if (isset(static::$dispatcher)) { + $name = get_called_class(); + static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback); + } + } + public function getObservableEvents() + { + return array('creating', 'created', 'updating', 'updated', 'deleting', 'deleted', 'saving', 'saved'); + } + protected function increment($column, $amount = 1) + { + return $this->incrementOrDecrement($column, $amount, 'increment'); + } + protected function decrement($column, $amount = 1) + { + return $this->incrementOrDecrement($column, $amount, 'decrement'); + } + protected function incrementOrDecrement($column, $amount, $method) + { + $query = $this->newQuery(); + if (!$this->exists) { + return $query->{$method}($column, $amount); + } + return $query->where($this->getKeyName(), $this->getKey())->{$method}($column, $amount); + } + public function update(array $attributes = array()) + { + if (!$this->exists) { + return $this->newQuery()->update($attributes); + } + return $this->fill($attributes)->save(); + } + public function push() + { + if (!$this->save()) { + return false; + } + foreach ($this->relations as $models) { + foreach (Collection::make($models) as $model) { + if (!$model->push()) { + return false; + } + } + } + return true; + } + public function save(array $options = array()) + { + $query = $this->newQueryWithDeleted(); + if ($this->fireModelEvent('saving') === false) { + return false; + } + if ($this->exists) { + $saved = $this->performUpdate($query); + } else { + $saved = $this->performInsert($query); + } + if ($saved) { + $this->finishSave($options); + } + return $saved; + } + protected function finishSave(array $options) + { + $this->syncOriginal(); + $this->fireModelEvent('saved', false); + if (array_get($options, 'touch', true)) { + $this->touchOwners(); + } + } + protected function performUpdate($query) + { + $dirty = $this->getDirty(); + if (count($dirty) > 0) { + if ($this->fireModelEvent('updating') === false) { + return false; + } + if ($this->timestamps) { + $this->updateTimestamps(); + $dirty = $this->getDirty(); + } + $this->setKeysForSaveQuery($query)->update($dirty); + $this->fireModelEvent('updated', false); + } + return true; + } + protected function performInsert($query) + { + if ($this->fireModelEvent('creating') === false) { + return false; + } + if ($this->timestamps) { + $this->updateTimestamps(); + } + $attributes = $this->attributes; + if ($this->incrementing) { + $this->insertAndSetId($query, $attributes); + } else { + $query->insert($attributes); + } + $this->exists = true; + $this->fireModelEvent('created', false); + return true; + } + protected function insertAndSetId($query, $attributes) + { + $id = $query->insertGetId($attributes, $keyName = $this->getKeyName()); + $this->setAttribute($keyName, $id); + } + public function touchOwners() + { + foreach ($this->touches as $relation) { + $this->{$relation}()->touch(); + } + } + public function touches($relation) + { + return in_array($relation, $this->touches); + } + protected function fireModelEvent($event, $halt = true) + { + if (!isset(static::$dispatcher)) { + return true; + } + $event = "eloquent.{$event}: " . get_class($this); + $method = $halt ? 'until' : 'fire'; + return static::$dispatcher->{$method}($event, $this); + } + protected function setKeysForSaveQuery($query) + { + $query->where($this->getKeyName(), '=', $this->getKey()); + return $query; + } + public function touch() + { + $this->updateTimestamps(); + return $this->save(); + } + protected function updateTimestamps() + { + $time = $this->freshTimestamp(); + if (!$this->isDirty(static::UPDATED_AT)) { + $this->setUpdatedAt($time); + } + if (!$this->exists and !$this->isDirty(static::CREATED_AT)) { + $this->setCreatedAt($time); + } + } + public function setCreatedAt($value) + { + $this->{static::CREATED_AT} = $value; + } + public function setUpdatedAt($value) + { + $this->{static::UPDATED_AT} = $value; + } + public function getCreatedAtColumn() + { + return static::CREATED_AT; + } + public function getUpdatedAtColumn() + { + return static::UPDATED_AT; + } + public function getDeletedAtColumn() + { + return static::DELETED_AT; + } + public function getQualifiedDeletedAtColumn() + { + return $this->getTable() . '.' . $this->getDeletedAtColumn(); + } + public function freshTimestamp() + { + return new DateTime(); + } + public function newQuery($excludeDeleted = true) + { + $builder = new Builder($this->newBaseQueryBuilder()); + $builder->setModel($this)->with($this->with); + if ($excludeDeleted and $this->softDelete) { + $builder->whereNull($this->getQualifiedDeletedAtColumn()); + } + return $builder; + } + public function newQueryWithDeleted() + { + return $this->newQuery(false); + } + public function trashed() + { + return $this->softDelete and !is_null($this->{static::DELETED_AT}); + } + public static function withTrashed() + { + return with(new static())->newQueryWithDeleted(); + } + public static function onlyTrashed() + { + $instance = new static(); + $column = $instance->getQualifiedDeletedAtColumn(); + return $instance->newQueryWithDeleted()->whereNotNull($column); + } + protected function newBaseQueryBuilder() + { + $conn = $this->getConnection(); + $grammar = $conn->getQueryGrammar(); + return new QueryBuilder($conn, $grammar, $conn->getPostProcessor()); + } + public function newCollection(array $models = array()) + { + return new Collection($models); + } + public function getTable() + { + if (isset($this->table)) { + return $this->table; + } + return str_replace('\\', '', snake_case(str_plural(get_class($this)))); + } + public function setTable($table) + { + $this->table = $table; + } + public function getKey() + { + return $this->getAttribute($this->getKeyName()); + } + public function getKeyName() + { + return $this->primaryKey; + } + public function getQualifiedKeyName() + { + return $this->getTable() . '.' . $this->getKeyName(); + } + public function usesTimestamps() + { + return $this->timestamps; + } + public function isSoftDeleting() + { + return $this->softDelete; + } + public function setSoftDeleting($enabled) + { + $this->softDelete = $enabled; + } + protected function getMorphs($name, $type, $id) + { + $type = $type ?: $name . '_type'; + $id = $id ?: $name . '_id'; + return array($type, $id); + } + public function getPerPage() + { + return $this->perPage; + } + public function setPerPage($perPage) + { + $this->perPage = $perPage; + } + public function getForeignKey() + { + return snake_case(class_basename($this)) . '_id'; + } + public function getHidden() + { + return $this->hidden; + } + public function setHidden(array $hidden) + { + $this->hidden = $hidden; + } + public function setVisible(array $visible) + { + $this->visible = $visible; + } + public function getFillable() + { + return $this->fillable; + } + public function fillable(array $fillable) + { + $this->fillable = $fillable; + return $this; + } + public function guard(array $guarded) + { + $this->guarded = $guarded; + return $this; + } + public static function unguard() + { + static::$unguarded = true; + } + public static function reguard() + { + static::$unguarded = false; + } + public static function setUnguardState($state) + { + static::$unguarded = $state; + } + public function isFillable($key) + { + if (static::$unguarded) { + return true; + } + if (in_array($key, $this->fillable)) { + return true; + } + if ($this->isGuarded($key)) { + return false; + } + return empty($this->fillable) and !starts_with($key, '_'); + } + public function isGuarded($key) + { + return in_array($key, $this->guarded) or $this->guarded == array('*'); + } + public function totallyGuarded() + { + return count($this->fillable) == 0 and $this->guarded == array('*'); + } + protected function removeTableFromKey($key) + { + if (!str_contains($key, '.')) { + return $key; + } + return last(explode('.', $key)); + } + public function getTouchedRelations() + { + return $this->touches; + } + public function setTouchedRelations(array $touches) + { + $this->touches = $touches; + } + public function getIncrementing() + { + return $this->incrementing; + } + public function setIncrementing($value) + { + $this->incrementing = $value; + } + public function toJson($options = 0) + { + return json_encode($this->toArray(), $options); + } + public function toArray() + { + $attributes = $this->attributesToArray(); + return array_merge($attributes, $this->relationsToArray()); + } + public function attributesToArray() + { + $attributes = $this->getAccessibleAttributes(); + foreach ($this->getMutatedAttributes() as $key) { + if (!array_key_exists($key, $attributes)) { + continue; + } + $attributes[$key] = $this->mutateAttribute($key, $attributes[$key]); + } + return $attributes; + } + protected function getAccessibleAttributes() + { + if (count($this->visible) > 0) { + return array_intersect_key($this->attributes, array_flip($this->visible)); + } + return array_diff_key($this->attributes, array_flip($this->hidden)); + } + public function relationsToArray() + { + $attributes = array(); + foreach ($this->relations as $key => $value) { + if (in_array($key, $this->hidden)) { + continue; + } + if ($value instanceof ArrayableInterface) { + $relation = $value->toArray(); + } elseif (is_null($value)) { + $relation = $value; + } + if (static::$snakeAttributes) { + $key = snake_case($key); + } + if (isset($relation)) { + $attributes[$key] = $relation; + } + } + return $attributes; + } + public function getAttribute($key) + { + $inAttributes = array_key_exists($key, $this->attributes); + if ($inAttributes or $this->hasGetMutator($key)) { + return $this->getAttributeValue($key); + } + if (array_key_exists($key, $this->relations)) { + return $this->relations[$key]; + } + $camelKey = camel_case($key); + if (method_exists($this, $camelKey)) { + $relations = $this->{$camelKey}()->getResults(); + return $this->relations[$key] = $relations; + } + } + protected function getAttributeValue($key) + { + $value = $this->getAttributeFromArray($key); + if ($this->hasGetMutator($key)) { + return $this->mutateAttribute($key, $value); + } elseif (in_array($key, $this->getDates())) { + if ($value) { + return $this->asDateTime($value); + } + } + return $value; + } + protected function getAttributeFromArray($key) + { + if (array_key_exists($key, $this->attributes)) { + return $this->attributes[$key]; + } + } + public function hasGetMutator($key) + { + return method_exists($this, 'get' . studly_case($key) . 'Attribute'); + } + protected function mutateAttribute($key, $value) + { + return $this->{'get' . studly_case($key) . 'Attribute'}($value); + } + public function setAttribute($key, $value) + { + if ($this->hasSetMutator($key)) { + $method = 'set' . studly_case($key) . 'Attribute'; + return $this->{$method}($value); + } elseif (in_array($key, $this->getDates())) { + if ($value) { + $value = $this->fromDateTime($value); + } + } + $this->attributes[$key] = $value; + } + public function hasSetMutator($key) + { + return method_exists($this, 'set' . studly_case($key) . 'Attribute'); + } + public function getDates() + { + return array(static::CREATED_AT, static::UPDATED_AT, static::DELETED_AT); + } + protected function fromDateTime($value) + { + $format = $this->getDateFormat(); + if ($value instanceof DateTime) { + + } elseif (is_numeric($value)) { + $value = Carbon::createFromTimestamp($value); + } elseif (preg_match('/^(\\d{4})-(\\d{2})-(\\d{2})$/', $value)) { + $value = Carbon::createFromFormat('Y-m-d', $value); + } elseif (!$value instanceof DateTime) { + $value = Carbon::createFromFormat($format, $value); + } + return $value->format($format); + } + protected function asDateTime($value) + { + if (is_numeric($value)) { + return Carbon::createFromTimestamp($value); + } elseif (preg_match('/^(\\d{4})-(\\d{2})-(\\d{2})$/', $value)) { + return Carbon::createFromFormat('Y-m-d', $value); + } elseif (!$value instanceof DateTime) { + $format = $this->getDateFormat(); + return Carbon::createFromFormat($format, $value); + } + return Carbon::instance($value); + } + protected function getDateFormat() + { + return $this->getConnection()->getQueryGrammar()->getDateFormat(); + } + public function replicate() + { + $attributes = array_except($this->attributes, array($this->getKeyName())); + with($instance = new static())->setRawAttributes($attributes); + return $instance->setRelations($this->relations); + } + public function getAttributes() + { + return $this->attributes; + } + public function setRawAttributes(array $attributes, $sync = false) + { + $this->attributes = $attributes; + if ($sync) { + $this->syncOriginal(); + } + } + public function getOriginal($key = null, $default = null) + { + return array_get($this->original, $key, $default); + } + public function syncOriginal() + { + $this->original = $this->attributes; + return $this; + } + public function isDirty($attribute) + { + return array_key_exists($attribute, $this->getDirty()); + } + public function getDirty() + { + $dirty = array(); + foreach ($this->attributes as $key => $value) { + if (!array_key_exists($key, $this->original) or $value !== $this->original[$key]) { + $dirty[$key] = $value; + } + } + return $dirty; + } + public function getRelation($relation) + { + return $this->relations[$relation]; + } + public function setRelation($relation, $value) + { + $this->relations[$relation] = $value; + return $this; + } + public function setRelations(array $relations) + { + $this->relations = $relations; + return $this; + } + public function getConnection() + { + return static::resolveConnection($this->connection); + } + public function getConnectionName() + { + return $this->connection; + } + public function setConnection($name) + { + $this->connection = $name; + } + public static function resolveConnection($connection = null) + { + return static::$resolver->connection($connection); + } + public static function getConnectionResolver() + { + return static::$resolver; + } + public static function setConnectionResolver(Resolver $resolver) + { + static::$resolver = $resolver; + } + public static function getEventDispatcher() + { + return static::$dispatcher; + } + public static function setEventDispatcher(Dispatcher $dispatcher) + { + static::$dispatcher = $dispatcher; + } + public static function unsetEventDispatcher() + { + static::$dispatcher = null; + } + public function getMutatedAttributes() + { + $class = get_class($this); + if (isset(static::$mutatorCache[$class])) { + return static::$mutatorCache[get_class($this)]; + } + return array(); + } + public function __get($key) + { + return $this->getAttribute($key); + } + public function __set($key, $value) + { + $this->setAttribute($key, $value); + } + public function offsetExists($offset) + { + return isset($this->{$offset}); + } + public function offsetGet($offset) + { + return $this->{$offset}; + } + public function offsetSet($offset, $value) + { + $this->{$offset} = $value; + } + public function offsetUnset($offset) + { + unset($this->{$offset}); + } + public function __isset($key) + { + return isset($this->attributes[$key]) or isset($this->relations[$key]); + } + public function __unset($key) + { + unset($this->attributes[$key]); + unset($this->relations[$key]); + } + public function __call($method, $parameters) + { + if (in_array($method, array('increment', 'decrement'))) { + return call_user_func_array(array($this, $method), $parameters); + } + $query = $this->newQuery(); + return call_user_func_array(array($query, $method), $parameters); + } + public static function __callStatic($method, $parameters) + { + $instance = new static(); + return call_user_func_array(array($instance, $method), $parameters); + } + public function __toString() + { + return $this->toJson(); + } +} +namespace Illuminate\Support\Contracts; + +interface ArrayableInterface +{ + public function toArray(); +} +namespace Illuminate\Support\Contracts; + +interface JsonableInterface +{ + public function toJson($options = 0); +} +namespace Illuminate\Database; + +use Illuminate\Support\Manager; +use Illuminate\Database\Connectors\ConnectionFactory; +class DatabaseManager implements ConnectionResolverInterface +{ + protected $app; + protected $factory; + protected $connections = array(); + protected $extensions = array(); + public function __construct($app, ConnectionFactory $factory) + { + $this->app = $app; + $this->factory = $factory; + } + public function connection($name = null) + { + $name = $name ?: $this->getDefaultConnection(); + if (!isset($this->connections[$name])) { + $connection = $this->makeConnection($name); + $this->connections[$name] = $this->prepare($connection); + } + return $this->connections[$name]; + } + public function reconnect($name = null) + { + unset($this->connections[$name]); + return $this->connection($name); + } + protected function makeConnection($name) + { + $config = $this->getConfig($name); + if (isset($this->extensions[$name])) { + return call_user_func($this->extensions[$name], $config); + } + return $this->factory->make($config, $name); + } + protected function prepare(Connection $connection) + { + $connection->setFetchMode($this->app['config']['database.fetch']); + if ($this->app->bound('events')) { + $connection->setEventDispatcher($this->app['events']); + } + $app = $this->app; + $connection->setCacheManager(function () use($app) { + return $app['cache']; + }); + $connection->setPaginator(function () use($app) { + return $app['paginator']; + }); + return $connection; + } + protected function getConfig($name) + { + $name = $name ?: $this->getDefaultConnection(); + $connections = $this->app['config']['database.connections']; + if (is_null($config = array_get($connections, $name))) { + throw new \InvalidArgumentException("Database [{$name}] not configured."); + } + return $config; + } + public function getDefaultConnection() + { + return $this->app['config']['database.default']; + } + public function setDefaultConnection($name) + { + $this->app['config']['database.default'] = $name; + } + public function extend($name, $resolver) + { + $this->extensions[$name] = $resolver; + } + public function __call($method, $parameters) + { + return call_user_func_array(array($this->connection(), $method), $parameters); + } +} +namespace Illuminate\Database; + +interface ConnectionResolverInterface +{ + public function connection($name = null); + public function getDefaultConnection(); + public function setDefaultConnection($name); +} +namespace Illuminate\Database\Connectors; + +use PDO; +use Illuminate\Container\Container; +use Illuminate\Database\MySqlConnection; +use Illuminate\Database\SQLiteConnection; +use Illuminate\Database\PostgresConnection; +use Illuminate\Database\SqlServerConnection; +class ConnectionFactory +{ + protected $container; + public function __construct(Container $container) + { + $this->container = $container; + } + public function make(array $config, $name = null) + { + $config = $this->parseConfig($config, $name); + $pdo = $this->createConnector($config)->connect($config); + return $this->createConnection($config['driver'], $pdo, $config['database'], $config['prefix'], $config); + } + protected function parseConfig(array $config, $name) + { + return array_add(array_add($config, 'prefix', ''), 'name', $name); + } + public function createConnector(array $config) + { + if (!isset($config['driver'])) { + throw new \InvalidArgumentException('A driver must be specified.'); + } + switch ($config['driver']) { + case 'mysql': + return new MySqlConnector(); + case 'pgsql': + return new PostgresConnector(); + case 'sqlite': + return new SQLiteConnector(); + case 'sqlsrv': + return new SqlServerConnector(); + } + throw new \InvalidArgumentException("Unsupported driver [{$config['driver']}]"); + } + protected function createConnection($driver, PDO $connection, $database, $prefix = '', $config = null) + { + if ($this->container->bound($key = "db.connection.{$driver}")) { + return $this->container->make($key, array($connection, $database, $prefix, $config)); + } + switch ($driver) { + case 'mysql': + return new MySqlConnection($connection, $database, $prefix, $config); + case 'pgsql': + return new PostgresConnection($connection, $database, $prefix, $config); + case 'sqlite': + return new SQLiteConnection($connection, $database, $prefix, $config); + case 'sqlsrv': + return new SqlServerConnection($connection, $database, $prefix, $config); + } + throw new \InvalidArgumentException("Unsupported driver [{$driver}]"); + } +} +namespace Illuminate\Session; + +use Symfony\Component\HttpFoundation\Session\Session as SymfonySession; +class Store extends SymfonySession +{ + public function start() + { + parent::start(); + if (!$this->has('_token')) { + $this->put('_token', str_random(40)); + } + } + public function save() + { + $this->ageFlashData(); + return parent::save(); + } + protected function ageFlashData() + { + foreach ($this->get('flash.old', array()) as $old) { + $this->forget($old); + } + $this->put('flash.old', $this->get('flash.new', array())); + $this->put('flash.new', array()); + } + public function has($name) + { + return !is_null($this->get($name)); + } + public function get($name, $default = null) + { + return array_get($this->all(), $name, $default); + } + public function hasOldInput($key) + { + return !is_null($this->getOldInput($key)); + } + public function getOldInput($key = null, $default = null) + { + $input = $this->get('_old_input', array()); + if (is_null($key)) { + return $input; + } + return array_get($input, $key, $default); + } + public function getToken() + { + return $this->token(); + } + public function token() + { + return $this->get('_token'); + } + public function put($key, $value) + { + $all = $this->all(); + array_set($all, $key, $value); + $this->replace($all); + } + public function push($key, $value) + { + $array = $this->get($key, array()); + $array[] = $value; + $this->put($key, $array); + } + public function flash($key, $value) + { + $this->put($key, $value); + $this->push('flash.new', $key); + $this->removeFromOldFlashData(array($key)); + } + public function flashInput(array $value) + { + return $this->flash('_old_input', $value); + } + public function reflash() + { + $this->mergeNewFlashes($this->get('flash.old')); + $this->put('flash.old', array()); + } + public function keep($keys = null) + { + $keys = is_array($keys) ? $keys : func_get_args(); + $this->mergeNewFlashes($keys); + $this->removeFromOldFlashData($keys); + } + protected function mergeNewFlashes(array $keys) + { + $values = array_unique(array_merge($this->get('flash.new'), $keys)); + $this->put('flash.new', $values); + } + protected function removeFromOldFlashData(array $keys) + { + $this->put('flash.old', array_diff($this->get('flash.old', array()), $keys)); + } + public function forget($key) + { + $all = $this->all(); + array_forget($all, $key); + $this->replace($all); + } + public function flush() + { + return $this->clear(); + } + public function regenerate() + { + return $this->migrate(); + } +} +namespace Illuminate\Session; + +use Illuminate\Support\Manager; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; +class SessionManager extends Manager +{ + protected function callCustomCreator($driver) + { + return $this->buildSession(parent::callCustomCreator($driver)); + } + protected function createArrayDriver() + { + return new Store(new MockArraySessionStorage()); + } + protected function createCookieDriver() + { + $lifetime = $this->app['config']['session.lifetime']; + return $this->buildSession(new CookieSessionHandler($this->app['cookie'], $lifetime)); + } + protected function createNativeDriver() + { + $path = $this->app['config']['session.files']; + return $this->buildSession(new NativeFileSessionHandler($path)); + } + protected function createDatabaseDriver() + { + $connection = $this->getDatabaseConnection(); + $table = $connection->getTablePrefix() . $this->app['config']['session.table']; + return $this->buildSession(new PdoSessionHandler($connection->getPdo(), $this->getDatabaseOptions($table))); + } + protected function getDatabaseConnection() + { + $connection = $this->app['config']['session.connection']; + return $this->app['db']->connection($connection); + } + protected function getDatabaseOptions($table) + { + return array('db_table' => $table, 'db_id_col' => 'id', 'db_data_col' => 'payload', 'db_time_col' => 'last_activity'); + } + protected function createApcDriver() + { + return $this->createCacheBased('apc'); + } + protected function createMemcachedDriver() + { + return $this->createCacheBased('memcached'); + } + protected function createWincacheDriver() + { + return $this->createCacheBased('wincache'); + } + protected function createRedisDriver() + { + return $this->createCacheBased('redis'); + } + protected function createCacheBased($driver) + { + $minutes = $this->app['config']['session.lifetime']; + $handler = new CacheBasedSessionHandler($this->app['cache']->driver($driver), $minutes); + return $this->buildSession($handler); + } + protected function buildSession($handler) + { + return new Store(new NativeSessionStorage($this->getOptions(), $handler)); + } + protected function getOptions() + { + $config = $this->app['config']['session']; + return array('cookie_domain' => $config['domain'], 'cookie_lifetime' => $config['lifetime'] * 60, 'cookie_path' => $config['path'], 'cookie_httponly' => '1', 'name' => $config['cookie'], 'gc_divisor' => $config['lottery'][1], 'gc_probability' => $config['lottery'][0]); + } + protected function getDefaultDriver() + { + return $this->app['config']['session.driver']; + } +} +namespace Illuminate\Support; + +use Closure; +abstract class Manager +{ + protected $app; + protected $customCreators = array(); + protected $drivers = array(); + public function __construct($app) + { + $this->app = $app; + } + public function driver($driver = null) + { + $driver = $driver ?: $this->getDefaultDriver(); + if (!isset($this->drivers[$driver])) { + $this->drivers[$driver] = $this->createDriver($driver); + } + return $this->drivers[$driver]; + } + protected function createDriver($driver) + { + $method = 'create' . ucfirst($driver) . 'Driver'; + if (isset($this->customCreators[$driver])) { + return $this->callCustomCreator($driver); + } elseif (method_exists($this, $method)) { + return $this->{$method}(); + } + throw new \InvalidArgumentException("Driver [{$driver}] not supported."); + } + protected function callCustomCreator($driver) + { + return $this->customCreators[$driver]($this->app); + } + public function extend($driver, Closure $callback) + { + $this->customCreators[$driver] = $callback; + } + public function getDrivers() + { + return $this->drivers; + } + public function __call($method, $parameters) + { + return call_user_func_array(array($this->driver(), $method), $parameters); + } +} +namespace Illuminate\Cookie; + +use Closure; +use Illuminate\Encryption\Encrypter; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +class CookieJar +{ + protected $request; + protected $encrypter; + protected $path = '/'; + protected $domain = null; + public function __construct(Request $request, Encrypter $encrypter) + { + $this->request = $request; + $this->encrypter = $encrypter; + } + public function has($key) + { + return !is_null($this->get($key)); + } + public function get($key, $default = null) + { + $value = $this->request->cookies->get($key); + if (!is_null($value)) { + return $this->decrypt($value); + } + return $default instanceof Closure ? $default() : $default; + } + protected function decrypt($value) + { + try { + return $this->encrypter->decrypt($value); + } catch (\Exception $e) { + return null; + } + } + public function make($name, $value, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true) + { + list($path, $domain) = $this->getPathAndDomain($path, $domain); + $time = $minutes == 0 ? 0 : time() + $minutes * 60; + $value = $this->encrypter->encrypt($value); + return new Cookie($name, $value, $time, $path, $domain, $secure, $httpOnly); + } + public function forever($name, $value, $path = null, $domain = null, $secure = false, $httpOnly = true) + { + return $this->make($name, $value, 2628000, $path, $domain, $secure, $httpOnly); + } + public function forget($name) + { + return $this->make($name, null, -2628000); + } + protected function getPathAndDomain($path, $domain) + { + return array($path ?: $this->path, $domain ?: $this->domain); + } + public function setDefaultPathAndDomain($path, $domain) + { + list($this->path, $this->domain) = array($path, $domain); + return $this; + } + public function getRequest() + { + return $this->request; + } + public function getEncrypter() + { + return $this->encrypter; + } +} +namespace Illuminate\Encryption; + +class DecryptException extends \RuntimeException +{ + +} +class Encrypter +{ + protected $key; + protected $cipher = 'rijndael-256'; + protected $mode = 'cbc'; + protected $block = 32; + public function __construct($key) + { + $this->key = $key; + } + public function encrypt($value) + { + $iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer()); + $value = base64_encode($this->padAndMcrypt($value, $iv)); + $mac = $this->hash($iv = base64_encode($iv), $value); + return base64_encode(json_encode(compact('iv', 'value', 'mac'))); + } + protected function padAndMcrypt($value, $iv) + { + $value = $this->addPadding(serialize($value)); + return mcrypt_encrypt($this->cipher, $this->key, $value, $this->mode, $iv); + } + public function decrypt($payload) + { + $payload = $this->getJsonPayload($payload); + $value = base64_decode($payload['value']); + $iv = base64_decode($payload['iv']); + return unserialize($this->stripPadding($this->mcryptDecrypt($value, $iv))); + } + protected function mcryptDecrypt($value, $iv) + { + return mcrypt_decrypt($this->cipher, $this->key, $value, $this->mode, $iv); + } + protected function getJsonPayload($payload) + { + $payload = json_decode(base64_decode($payload), true); + if (!$payload or $this->invalidPayload($payload)) { + throw new DecryptException('Invalid data.'); + } + if ($payload['mac'] !== $this->hash($payload['iv'], $payload['value'])) { + throw new DecryptException('MAC is invalid.'); + } + return $payload; + } + protected function hash($iv, $value) + { + return hash_hmac('sha256', $iv . $value, $this->key); + } + protected function addPadding($value) + { + $pad = $this->block - strlen($value) % $this->block; + return $value . str_repeat(chr($pad), $pad); + } + protected function stripPadding($value) + { + $pad = ord($value[($len = strlen($value)) - 1]); + return $this->paddingIsValid($pad, $value) ? substr($value, 0, strlen($value) - $pad) : $value; + } + protected function paddingIsValid($pad, $value) + { + $beforePad = strlen($value) - $pad; + return substr($value, $beforePad) == str_repeat(substr($value, -1), $pad); + } + protected function invalidPayload(array $data) + { + return !isset($data['iv']) or !isset($data['value']) or !isset($data['mac']); + } + protected function getIvSize() + { + return mcrypt_get_iv_size($this->cipher, $this->mode); + } + protected function getRandomizer() + { + if (defined('MCRYPT_DEV_URANDOM')) { + return MCRYPT_DEV_URANDOM; + } + if (defined('MCRYPT_DEV_RANDOM')) { + return MCRYPT_DEV_RANDOM; + } + mt_srand(); + return MCRYPT_RAND; + } + public function setKey($key) + { + $this->key = $key; + } + public function setCipher($cipher) + { + $this->cipher = $cipher; + } + public function setMode($mode) + { + $this->mode = $mode; + } +} +namespace Illuminate\Support\Facades; + +class Log extends Facade +{ + protected static function getFacadeAccessor() + { + return 'log'; + } +} +namespace Illuminate\Log; + +use Illuminate\Support\ServiceProvider; +class LogServiceProvider extends ServiceProvider +{ + protected $defer = true; + public function register() + { + $logger = new Writer(new \Monolog\Logger('log'), $this->app['events']); + $this->app->instance('log', $logger); + if (isset($this->app['log.setup'])) { + call_user_func($this->app['log.setup'], $logger); + } + } + public function provides() + { + return array('log'); + } +} +namespace Illuminate\Log; + +use Closure; +use Illuminate\Events\Dispatcher; +use Monolog\Handler\StreamHandler; +use Monolog\Logger as MonologLogger; +use Monolog\Handler\RotatingFileHandler; +class Writer +{ + protected $monolog; + protected $levels = array('debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency'); + protected $dispatcher; + public function __construct(MonologLogger $monolog, Dispatcher $dispatcher = null) + { + $this->monolog = $monolog; + if (isset($dispatcher)) { + $this->dispatcher = $dispatcher; + } + } + public function useFiles($path, $level = 'debug') + { + $level = $this->parseLevel($level); + $this->monolog->pushHandler(new StreamHandler($path, $level)); + } + public function useDailyFiles($path, $days = 0, $level = 'debug') + { + $level = $this->parseLevel($level); + $this->monolog->pushHandler(new RotatingFileHandler($path, $days, $level)); + } + protected function parseLevel($level) + { + switch ($level) { + case 'debug': + return MonologLogger::DEBUG; + case 'info': + return MonologLogger::INFO; + case 'notice': + return MonologLogger::NOTICE; + case 'warning': + return MonologLogger::WARNING; + case 'error': + return MonologLogger::ERROR; + case 'critical': + return MonologLogger::CRITICAL; + case 'alert': + return MonologLogger::ALERT; + case 'emergency': + return MonologLogger::EMERGENCY; + default: + throw new \InvalidArgumentException('Invalid log level.'); + } + } + public function getMonolog() + { + return $this->monolog; + } + public function listen(Closure $callback) + { + if (!isset($this->dispatcher)) { + throw new \RuntimeException('Events dispatcher has not been set.'); + } + $this->dispatcher->listen('illuminate.log', $callback); + } + public function getEventDispatcher() + { + return $this->dispatcher; + } + public function setEventDispatcher(Dispatcher $dispatcher) + { + $this->dispatcher = $dispatcher; + } + protected function fireLogEvent($level, $message, array $context = array()) + { + if (isset($this->dispatcher)) { + $this->dispatcher->fire('illuminate.log', compact('level', 'message', 'context')); + } + } + public function __call($method, $parameters) + { + if (in_array($method, $this->levels)) { + call_user_func_array(array($this, 'fireLogEvent'), array_merge(array($method), $parameters)); + $method = 'add' . ucfirst($method); + return call_user_func_array(array($this->monolog, $method), $parameters); + } + throw new \BadMethodCallException("Method [{$method}] does not exist."); + } +} +namespace Monolog; + +use Monolog\Handler\HandlerInterface; +use Monolog\Handler\StreamHandler; +use Psr\Log\LoggerInterface; +use Psr\Log\InvalidArgumentException; +class Logger implements LoggerInterface +{ + const DEBUG = 100; + const INFO = 200; + const NOTICE = 250; + const WARNING = 300; + const ERROR = 400; + const CRITICAL = 500; + const ALERT = 550; + const EMERGENCY = 600; + protected static $levels = array(100 => 'DEBUG', 200 => 'INFO', 250 => 'NOTICE', 300 => 'WARNING', 400 => 'ERROR', 500 => 'CRITICAL', 550 => 'ALERT', 600 => 'EMERGENCY'); + protected static $timezone; + protected $name; + protected $handlers; + protected $processors; + public function __construct($name, array $handlers = array(), array $processors = array()) + { + $this->name = $name; + $this->handlers = $handlers; + $this->processors = $processors; + } + public function getName() + { + return $this->name; + } + public function pushHandler(HandlerInterface $handler) + { + array_unshift($this->handlers, $handler); + } + public function popHandler() + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + return array_shift($this->handlers); + } + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), ' . var_export($callback, true) . ' given'); + } + array_unshift($this->processors, $callback); + } + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + return array_shift($this->processors); + } + public function addRecord($level, $message, array $context = array()) + { + if (!$this->handlers) { + $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG)); + } + if (!static::$timezone) { + static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC'); + } + $record = array('message' => (string) $message, 'context' => $context, 'level' => $level, 'level_name' => static::getLevelName($level), 'channel' => $this->name, 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone)->setTimezone(static::$timezone), 'extra' => array()); + $handlerKey = null; + foreach ($this->handlers as $key => $handler) { + if ($handler->isHandling($record)) { + $handlerKey = $key; + break; + } + } + if (null === $handlerKey) { + return false; + } + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + while (isset($this->handlers[$handlerKey]) && false === $this->handlers[$handlerKey]->handle($record)) { + $handlerKey++; + } + return true; + } + public function addDebug($message, array $context = array()) + { + return $this->addRecord(static::DEBUG, $message, $context); + } + public function addInfo($message, array $context = array()) + { + return $this->addRecord(static::INFO, $message, $context); + } + public function addNotice($message, array $context = array()) + { + return $this->addRecord(static::NOTICE, $message, $context); + } + public function addWarning($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + public function addError($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + public function addCritical($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + public function addAlert($message, array $context = array()) + { + return $this->addRecord(static::ALERT, $message, $context); + } + public function addEmergency($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + public static function getLevels() + { + return array_flip(static::$levels); + } + public static function getLevelName($level) + { + if (!isset(static::$levels[$level])) { + throw new InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . implode(', ', array_keys(static::$levels))); + } + return static::$levels[$level]; + } + public function isHandling($level) + { + $record = array('level' => $level); + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + return false; + } + public function log($level, $message, array $context = array()) + { + if (is_string($level) && defined(__CLASS__ . '::' . strtoupper($level))) { + $level = constant(__CLASS__ . '::' . strtoupper($level)); + } + return $this->addRecord($level, $message, $context); + } + public function debug($message, array $context = array()) + { + return $this->addRecord(static::DEBUG, $message, $context); + } + public function info($message, array $context = array()) + { + return $this->addRecord(static::INFO, $message, $context); + } + public function notice($message, array $context = array()) + { + return $this->addRecord(static::NOTICE, $message, $context); + } + public function warn($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + public function warning($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + public function err($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + public function error($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + public function crit($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + public function critical($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + public function alert($message, array $context = array()) + { + return $this->addRecord(static::ALERT, $message, $context); + } + public function emerg($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + public function emergency($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } +} +namespace Psr\Log; + +interface LoggerInterface +{ + public function emergency($message, array $context = array()); + public function alert($message, array $context = array()); + public function critical($message, array $context = array()); + public function error($message, array $context = array()); + public function warning($message, array $context = array()); + public function notice($message, array $context = array()); + public function info($message, array $context = array()); + public function debug($message, array $context = array()); + public function log($level, $message, array $context = array()); +} +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +abstract class AbstractHandler implements HandlerInterface +{ + protected $level = Logger::DEBUG; + protected $bubble = false; + protected $formatter; + protected $processors = array(); + public function __construct($level = Logger::DEBUG, $bubble = true) + { + $this->level = $level; + $this->bubble = $bubble; + } + public function isHandling(array $record) + { + return $record['level'] >= $this->level; + } + public function handleBatch(array $records) + { + foreach ($records as $record) { + $this->handle($record); + } + } + public function close() + { + + } + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), ' . var_export($callback, true) . ' given'); + } + array_unshift($this->processors, $callback); + } + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + return array_shift($this->processors); + } + public function setFormatter(FormatterInterface $formatter) + { + $this->formatter = $formatter; + } + public function getFormatter() + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + return $this->formatter; + } + public function setLevel($level) + { + $this->level = $level; + } + public function getLevel() + { + return $this->level; + } + public function setBubble($bubble) + { + $this->bubble = $bubble; + } + public function getBubble() + { + return $this->bubble; + } + public function __destruct() + { + try { + $this->close(); + } catch (\Exception $e) { + + } + } + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} +namespace Monolog\Handler; + +abstract class AbstractProcessingHandler extends AbstractHandler +{ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + $record = $this->processRecord($record); + $record['formatted'] = $this->getFormatter()->format($record); + $this->write($record); + return false === $this->bubble; + } + protected abstract function write(array $record); + protected function processRecord(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + return $record; + } +} +namespace Monolog\Handler; + +use Monolog\Logger; +class StreamHandler extends AbstractProcessingHandler +{ + protected $stream; + protected $url; + public function __construct($stream, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + if (is_resource($stream)) { + $this->stream = $stream; + } else { + $this->url = $stream; + } + } + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + } + protected function write(array $record) + { + if (null === $this->stream) { + if (!$this->url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); + } + $errorMessage = null; + set_error_handler(function ($code, $msg) use(&$errorMessage) { + $errorMessage = preg_replace('{^fopen\\(.*?\\): }', '', $msg); + }); + $this->stream = fopen($this->url, 'a'); + restore_error_handler(); + if (!is_resource($this->stream)) { + $this->stream = null; + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: ' . $errorMessage, $this->url)); + } + } + fwrite($this->stream, (string) $record['formatted']); + } +} +namespace Monolog\Handler; + +use Monolog\Logger; +class RotatingFileHandler extends StreamHandler +{ + protected $filename; + protected $maxFiles; + protected $mustRotate; + protected $nextRotation; + public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true) + { + $this->filename = $filename; + $this->maxFiles = (int) $maxFiles; + $this->nextRotation = new \DateTime('tomorrow'); + parent::__construct($this->getTimedFilename(), $level, $bubble); + } + public function close() + { + parent::close(); + if (true === $this->mustRotate) { + $this->rotate(); + } + } + protected function write(array $record) + { + if (null === $this->mustRotate) { + $this->mustRotate = !file_exists($this->url); + } + if ($this->nextRotation < $record['datetime']) { + $this->mustRotate = true; + $this->close(); + } + parent::write($record); + } + protected function rotate() + { + $this->url = $this->getTimedFilename(); + $this->nextRotation = new \DateTime('tomorrow'); + if (0 === $this->maxFiles) { + return; + } + $fileInfo = pathinfo($this->filename); + $glob = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '-*'; + if (!empty($fileInfo['extension'])) { + $glob .= '.' . $fileInfo['extension']; + } + $iterator = new \GlobIterator($glob); + $count = $iterator->count(); + if ($this->maxFiles >= $count) { + return; + } + $array = iterator_to_array($iterator); + usort($array, function ($a, $b) { + return strcmp($b->getFilename(), $a->getFilename()); + }); + foreach (array_slice($array, $this->maxFiles) as $file) { + if ($file->isWritable()) { + unlink($file->getRealPath()); + } + } + } + protected function getTimedFilename() + { + $fileInfo = pathinfo($this->filename); + $timedFilename = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '-' . date('Y-m-d'); + if (!empty($fileInfo['extension'])) { + $timedFilename .= '.' . $fileInfo['extension']; + } + return $timedFilename; + } +} +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +interface HandlerInterface +{ + public function isHandling(array $record); + public function handle(array $record); + public function handleBatch(array $records); + public function pushProcessor($callback); + public function popProcessor(); + public function setFormatter(FormatterInterface $formatter); + public function getFormatter(); +} +namespace Illuminate\Support\Facades; + +class App extends Facade +{ + protected static function getFacadeAccessor() + { + return 'app'; + } +} +namespace Illuminate\Exception; + +use Exception; +interface ExceptionDisplayerInterface +{ + public function display(Exception $exception); +} +namespace Illuminate\Exception; + +use Exception; +use Symfony\Component\Debug\ExceptionHandler; +class SymfonyDisplayer implements ExceptionDisplayerInterface +{ + protected $symfony; + public function __construct(ExceptionHandler $symfony) + { + $this->symfony = $symfony; + } + public function display(Exception $exception) + { + $this->symfony->handle($exception); + } +} +namespace Illuminate\Exception; + +use Exception; +use Whoops\Run; +class WhoopsDisplayer implements ExceptionDisplayerInterface +{ + protected $whoops; + protected $runningInConsole; + public function __construct(Run $whoops, $runningInConsole) + { + $this->whoops = $whoops; + $this->runningInConsole = $runningInConsole; + } + public function display(Exception $exception) + { + if (!$this->runningInConsole and !headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + $this->whoops->handleException($exception); + } +} +namespace Illuminate\Exception; + +use Closure; +use ErrorException; +use ReflectionFunction; +use Illuminate\Support\Contracts\ResponsePreparerInterface; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\Debug\Exception\FatalErrorException as FatalError; +class Handler +{ + protected $responsePreparer; + protected $plainDisplayer; + protected $debugDisplayer; + protected $debug; + protected $handlers = array(); + protected $handled = array(); + public function __construct(ResponsePreparerInterface $responsePreparer, ExceptionDisplayerInterface $plainDisplayer, ExceptionDisplayerInterface $debugDisplayer, $debug = true) + { + $this->debug = $debug; + $this->plainDisplayer = $plainDisplayer; + $this->debugDisplayer = $debugDisplayer; + $this->responsePreparer = $responsePreparer; + } + public function register($environment) + { + $this->registerErrorHandler(); + $this->registerExceptionHandler(); + if ($environment != 'testing') { + $this->registerShutdownHandler(); + } + } + protected function registerErrorHandler() + { + set_error_handler(array($this, 'handleError')); + } + protected function registerExceptionHandler() + { + set_exception_handler(array($this, 'handleException')); + } + protected function registerShutdownHandler() + { + register_shutdown_function(array($this, 'handleShutdown')); + } + public function handleError($level, $message, $file, $line, $context) + { + if (error_reporting() & $level) { + $e = new ErrorException($message, $level, 0, $file, $line); + $this->handleException($e); + } + } + public function handleException($exception) + { + $response = $this->callCustomHandlers($exception); + if (!is_null($response)) { + $response = $this->prepareResponse($response); + $response->send(); + } else { + $this->displayException($exception); + } + $this->bail(); + } + public function handleShutdown() + { + $error = error_get_last(); + if (!is_null($error)) { + extract($error); + if (!$this->isFatal($type)) { + return; + } + $this->handleException(new FatalError($message, $type, 0, $file, $line)); + } + } + protected function isFatal($type) + { + return in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE)); + } + public function handleConsole($exception) + { + return $this->callCustomHandlers($exception, true); + } + protected function callCustomHandlers($exception, $fromConsole = false) + { + foreach ($this->handlers as $handler) { + if (!$this->handlesException($handler, $exception)) { + continue; + } elseif ($exception instanceof HttpExceptionInterface) { + $code = $exception->getStatusCode(); + } else { + $code = 500; + } + try { + $response = $handler($exception, $code, $fromConsole); + } catch (\Exception $e) { + $response = $this->formatException($e); + } + if (isset($response) and !is_null($response)) { + return $response; + } + } + } + protected function displayException($exception) + { + $displayer = $this->debug ? $this->debugDisplayer : $this->plainDisplayer; + $displayer->display($exception); + } + protected function handlesException(Closure $handler, $exception) + { + $reflection = new ReflectionFunction($handler); + return $reflection->getNumberOfParameters() == 0 or $this->hints($reflection, $exception); + } + protected function hints(ReflectionFunction $reflection, $exception) + { + $parameters = $reflection->getParameters(); + $expected = $parameters[0]; + return !$expected->getClass() or $expected->getClass()->isInstance($exception); + } + protected function formatException(\Exception $e) + { + $location = $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine(); + return 'Error in exception handler: ' . $location; + } + public function error(Closure $callback) + { + array_unshift($this->handlers, $callback); + } + public function pushError(Closure $callback) + { + $this->handlers[] = $callback; + } + protected function prepareResponse($response) + { + return $this->responsePreparer->prepareResponse($response); + } + protected function bail() + { + die(1); + } + public function setDebug($debug) + { + $this->debug = $debug; + } +} +namespace Illuminate\Support\Facades; + +class Route extends Facade +{ + public static function is($name) + { + return static::$app['router']->currentRouteNamed($name); + } + public static function uses($action) + { + return static::$app['router']->currentRouteUses($action); + } + protected static function getFacadeAccessor() + { + return 'router'; + } +} +namespace Symfony\Component\Routing; + +class Route implements \Serializable +{ + private $path = '/'; + private $host = ''; + private $schemes = array(); + private $methods = array(); + private $defaults = array(); + private $requirements = array(); + private $options = array(); + private $compiled; + public function __construct($path, array $defaults = array(), array $requirements = array(), array $options = array(), $host = '', $schemes = array(), $methods = array()) + { + $this->setPath($path); + $this->setDefaults($defaults); + $this->setRequirements($requirements); + $this->setOptions($options); + $this->setHost($host); + if ($schemes) { + $this->setSchemes($schemes); + } + if ($methods) { + $this->setMethods($methods); + } + } + public function serialize() + { + return serialize(array('path' => $this->path, 'host' => $this->host, 'defaults' => $this->defaults, 'requirements' => $this->requirements, 'options' => $this->options, 'schemes' => $this->schemes, 'methods' => $this->methods)); + } + public function unserialize($data) + { + $data = unserialize($data); + $this->path = $data['path']; + $this->host = $data['host']; + $this->defaults = $data['defaults']; + $this->requirements = $data['requirements']; + $this->options = $data['options']; + $this->schemes = $data['schemes']; + $this->methods = $data['methods']; + } + public function getPattern() + { + return $this->path; + } + public function setPattern($pattern) + { + return $this->setPath($pattern); + } + public function getPath() + { + return $this->path; + } + public function setPath($pattern) + { + $this->path = '/' . ltrim(trim($pattern), '/'); + $this->compiled = null; + return $this; + } + public function getHost() + { + return $this->host; + } + public function setHost($pattern) + { + $this->host = (string) $pattern; + $this->compiled = null; + return $this; + } + public function getSchemes() + { + return $this->schemes; + } + public function setSchemes($schemes) + { + $this->schemes = array_map('strtolower', (array) $schemes); + if ($this->schemes) { + $this->requirements['_scheme'] = implode('|', $this->schemes); + } else { + unset($this->requirements['_scheme']); + } + $this->compiled = null; + return $this; + } + public function getMethods() + { + return $this->methods; + } + public function setMethods($methods) + { + $this->methods = array_map('strtoupper', (array) $methods); + if ($this->methods) { + $this->requirements['_method'] = implode('|', $this->methods); + } else { + unset($this->requirements['_method']); + } + $this->compiled = null; + return $this; + } + public function getOptions() + { + return $this->options; + } + public function setOptions(array $options) + { + $this->options = array('compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'); + return $this->addOptions($options); + } + public function addOptions(array $options) + { + foreach ($options as $name => $option) { + $this->options[$name] = $option; + } + $this->compiled = null; + return $this; + } + public function setOption($name, $value) + { + $this->options[$name] = $value; + $this->compiled = null; + return $this; + } + public function getOption($name) + { + return isset($this->options[$name]) ? $this->options[$name] : null; + } + public function hasOption($name) + { + return array_key_exists($name, $this->options); + } + public function getDefaults() + { + return $this->defaults; + } + public function setDefaults(array $defaults) + { + $this->defaults = array(); + return $this->addDefaults($defaults); + } + public function addDefaults(array $defaults) + { + foreach ($defaults as $name => $default) { + $this->defaults[$name] = $default; + } + $this->compiled = null; + return $this; + } + public function getDefault($name) + { + return isset($this->defaults[$name]) ? $this->defaults[$name] : null; + } + public function hasDefault($name) + { + return array_key_exists($name, $this->defaults); + } + public function setDefault($name, $default) + { + $this->defaults[$name] = $default; + $this->compiled = null; + return $this; + } + public function getRequirements() + { + return $this->requirements; + } + public function setRequirements(array $requirements) + { + $this->requirements = array(); + return $this->addRequirements($requirements); + } + public function addRequirements(array $requirements) + { + foreach ($requirements as $key => $regex) { + $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); + } + $this->compiled = null; + return $this; + } + public function getRequirement($key) + { + return isset($this->requirements[$key]) ? $this->requirements[$key] : null; + } + public function hasRequirement($key) + { + return array_key_exists($key, $this->requirements); + } + public function setRequirement($key, $regex) + { + $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); + $this->compiled = null; + return $this; + } + public function compile() + { + if (null !== $this->compiled) { + return $this->compiled; + } + $class = $this->getOption('compiler_class'); + return $this->compiled = $class::compile($this); + } + private function sanitizeRequirement($key, $regex) + { + if (!is_string($regex)) { + throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" must be a string.', $key)); + } + if ('' !== $regex && '^' === $regex[0]) { + $regex = (string) substr($regex, 1); + } + if ('$' === substr($regex, -1)) { + $regex = substr($regex, 0, -1); + } + if ('' === $regex) { + throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key)); + } + if ('_scheme' === $key) { + $this->setSchemes(explode('|', $regex)); + } elseif ('_method' === $key) { + $this->setMethods(explode('|', $regex)); + } + return $regex; + } +} +namespace Illuminate\Routing; + +use Illuminate\Http\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Route as BaseRoute; +class Route extends BaseRoute +{ + protected $router; + protected $parameters; + protected $parsedParameters; + public function run(Request $request) + { + $this->parsedParameters = null; + $response = $this->callBeforeFilters($request); + if (!isset($response)) { + $response = $this->callCallable(); + } else { + $fromFilter = true; + } + $response = $this->router->prepare($response, $request); + if (!isset($fromFilter)) { + $this->callAfterFilters($request, $response); + } + return $response; + } + protected function callCallable() + { + $variables = array_values($this->getParametersWithoutDefaults()); + return call_user_func_array($this->getOption('_call'), $variables); + } + protected function callBeforeFilters(Request $request) + { + $before = $this->getAllBeforeFilters($request); + $response = null; + foreach ($before as $filter) { + $response = $this->callFilter($filter, $request); + if (!is_null($response)) { + return $response; + } + } + } + protected function getAllBeforeFilters(Request $request) + { + $before = $this->getBeforeFilters(); + return array_merge($before, $this->router->findPatternFilters($request)); + } + protected function callAfterFilters(Request $request, $response) + { + foreach ($this->getAfterFilters() as $filter) { + $this->callFilter($filter, $request, array($response)); + } + } + public function callFilter($name, Request $request, array $params = array()) + { + if (!$this->router->filtersEnabled()) { + return; + } + $merge = array($this->router->getCurrentRoute(), $request); + $params = array_merge($merge, $params); + list($name, $params) = $this->parseFilter($name, $params); + if (!is_null($callable = $this->router->getFilter($name))) { + return call_user_func_array($callable, $params); + } + } + protected function parseFilter($name, $parameters = array()) + { + if (str_contains($name, ':')) { + $segments = explode(':', $name); + $name = $segments[0]; + $arguments = explode(',', $segments[1]); + $parameters = array_merge($parameters, $arguments); + } + return array($name, $parameters); + } + public function getParameter($name, $default = null) + { + return array_get($this->getParameters(), $name, $default); + } + public function getParameters() + { + if (isset($this->parsedParameters)) { + return $this->parsedParameters; + } + $variables = $this->compile()->getVariables(); + $parameters = array(); + foreach ($variables as $variable) { + $parameters[$variable] = $this->resolveParameter($variable); + } + return $this->parsedParameters = $parameters; + } + protected function resolveParameter($key) + { + $value = $this->parameters[$key]; + if ($this->router->hasBinder($key)) { + return $this->router->performBinding($key, $value, $this); + } + return $value; + } + public function getParametersWithoutDefaults() + { + $parameters = $this->getParameters(); + foreach ($parameters as $key => $value) { + if ($this->isMissingDefault($key, $value)) { + unset($parameters[$key]); + } + } + return $parameters; + } + protected function isMissingDefault($key, $value) + { + return $this->isOptional($key) and is_null($value); + } + public function isOptional($key) + { + return array_key_exists($key, $this->getDefaults()); + } + public function getParameterKeys() + { + return $this->compile()->getVariables(); + } + public function where($name, $expression = null) + { + if (is_array($name)) { + return $this->setArrayOfWheres($name); + } + $this->setRequirement($name, $expression); + return $this; + } + protected function setArrayOfWheres(array $wheres) + { + foreach ($wheres as $name => $expression) { + $this->where($name, $expression); + } + return $this; + } + public function defaults($key, $value) + { + $this->setDefault($key, $value); + return $this; + } + public function before() + { + $this->setBeforeFilters(func_get_args()); + return $this; + } + public function after() + { + $this->setAfterFilters(func_get_args()); + return $this; + } + public function getAction() + { + return $this->getOption('_uses'); + } + public function getBeforeFilters() + { + return $this->getOption('_before') ?: array(); + } + public function setBeforeFilters($value) + { + $filters = is_string($value) ? explode('|', $value) : (array) $value; + $this->setOption('_before', array_merge($this->getBeforeFilters(), $filters)); + } + public function getAfterFilters() + { + return $this->getOption('_after') ?: array(); + } + public function setAfterFilters($value) + { + $filters = is_string($value) ? explode('|', $value) : (array) $value; + $this->setOption('_after', array_merge($this->getAfterFilters(), $filters)); + } + public function setParameters($parameters) + { + $this->parameters = $parameters; + } + public function setRouter(Router $router) + { + $this->router = $router; + return $this; + } +} +namespace Illuminate\View\Engines; + +use Closure; +class EngineResolver +{ + protected $resolvers = array(); + protected $resolved = array(); + public function register($engine, Closure $resolver) + { + $this->resolvers[$engine] = $resolver; + } + public function resolve($engine) + { + if (!isset($this->resolved[$engine])) { + $this->resolved[$engine] = call_user_func($this->resolvers[$engine]); + } + return $this->resolved[$engine]; + } +} +namespace Illuminate\View; + +interface ViewFinderInterface +{ + public function find($view); + public function addLocation($location); + public function addNamespace($namespace, $hint); + public function addExtension($extension); +} +namespace Illuminate\View; + +use Illuminate\Filesystem\Filesystem; +class FileViewFinder implements ViewFinderInterface +{ + protected $files; + protected $paths; + protected $hints = array(); + protected $extensions = array('blade.php', 'php'); + public function __construct(Filesystem $files, array $paths, array $extensions = null) + { + $this->files = $files; + $this->paths = $paths; + if (isset($extensions)) { + $this->extensions = $extensions; + } + } + public function find($name) + { + if (strpos($name, '::') !== false) { + return $this->findNamedPathView($name); + } + return $this->findInPaths($name, $this->paths); + } + protected function findNamedPathView($name) + { + list($namespace, $view) = $this->getNamespaceSegments($name); + return $this->findInPaths($view, $this->hints[$namespace]); + } + protected function getNamespaceSegments($name) + { + $segments = explode('::', $name); + if (count($segments) != 2) { + throw new \InvalidArgumentException("View [{$name}] has an invalid name."); + } + if (!isset($this->hints[$segments[0]])) { + throw new \InvalidArgumentException("No hint path defined for [{$segments[0]}]."); + } + return $segments; + } + protected function findInPaths($name, $paths) + { + foreach ((array) $paths as $path) { + foreach ($this->getPossibleViewFiles($name) as $file) { + if ($this->files->exists($viewPath = $path . '/' . $file)) { + return $viewPath; + } + } + } + throw new \InvalidArgumentException("View [{$name}] not found."); + } + protected function getPossibleViewFiles($name) + { + return array_map(function ($extension) use($name) { + return str_replace('.', '/', $name) . '.' . $extension; + }, $this->extensions); + } + public function addLocation($location) + { + $this->paths[] = $location; + } + public function addNamespace($namespace, $hints) + { + $hints = (array) $hints; + if (isset($this->hints[$namespace])) { + $hints = array_merge($this->hints[$namespace], $hints); + } + $this->hints[$namespace] = $hints; + } + public function addExtension($extension) + { + if (($index = array_search($extension, $this->extensions)) !== false) { + unset($this->extensions[$index]); + } + array_unshift($this->extensions, $extension); + } + public function getFilesystem() + { + return $this->files; + } + public function getPaths() + { + return $this->paths; + } + public function getHints() + { + return $this->hints; + } + public function getExtensions() + { + return $this->extensions; + } +} +namespace Illuminate\View; + +use Closure; +use Illuminate\Events\Dispatcher; +use Illuminate\Container\Container; +use Illuminate\View\Engines\EngineResolver; +use Illuminate\Support\Contracts\ArrayableInterface as Arrayable; +class Environment +{ + protected $engines; + protected $finder; + protected $events; + protected $container; + protected $shared = array(); + protected $names = array(); + protected $extensions = array('blade.php' => 'blade', 'php' => 'php'); + protected $composers = array(); + protected $sections = array(); + protected $sectionStack = array(); + protected $renderCount = 0; + public function __construct(EngineResolver $engines, ViewFinderInterface $finder, Dispatcher $events) + { + $this->finder = $finder; + $this->events = $events; + $this->engines = $engines; + $this->share('__env', $this); + } + public function make($view, $data = array(), $mergeData = array()) + { + $path = $this->finder->find($view); + $data = array_merge($mergeData, $this->parseData($data)); + return new View($this, $this->getEngineFromPath($path), $view, $path, $data); + } + protected function parseData($data) + { + return $data instanceof Arrayable ? $data->toArray() : $data; + } + public function of($view, $data = array()) + { + return $this->make($this->names[$view], $data); + } + public function name($view, $name) + { + $this->names[$name] = $view; + } + public function exists($view) + { + try { + $this->finder->find($view); + } catch (\InvalidArgumentException $e) { + return false; + } + return true; + } + public function renderEach($view, $data, $iterator, $empty = 'raw|') + { + $result = ''; + if (count($data) > 0) { + foreach ($data as $key => $value) { + $data = array('key' => $key, $iterator => $value); + $result .= $this->make($view, $data)->render(); + } + } else { + if (starts_with($empty, 'raw|')) { + $result = substr($empty, 4); + } else { + $result = $this->make($empty)->render(); + } + } + return $result; + } + protected function getEngineFromPath($path) + { + $engine = $this->extensions[$this->getExtension($path)]; + return $this->engines->resolve($engine); + } + protected function getExtension($path) + { + $extensions = array_keys($this->extensions); + return array_first($extensions, function ($key, $value) use($path) { + return ends_with($path, $value); + }); + } + public function share($key, $value = null) + { + if (!is_array($key)) { + return $this->shared[$key] = $value; + } + foreach ($key as $innerKey => $innerValue) { + $this->share($innerKey, $innerValue); + } + } + public function composer($views, $callback) + { + $composers = array(); + foreach ((array) $views as $view) { + $composers[] = $this->addComposer($view, $callback); + } + return $composers; + } + protected function addComposer($view, $callback) + { + if ($callback instanceof Closure) { + $this->events->listen('composing: ' . $view, $callback); + return $callback; + } elseif (is_string($callback)) { + return $this->addClassComposer($view, $callback); + } + } + protected function addClassComposer($view, $class) + { + $name = 'composing: ' . $view; + $callback = $this->buildClassComposerCallback($class); + $this->events->listen($name, $callback); + return $callback; + } + protected function buildClassComposerCallback($class) + { + $container = $this->container; + list($class, $method) = $this->parseClassComposer($class); + return function () use($class, $method, $container) { + $callable = array($container->make($class), $method); + return call_user_func_array($callable, func_get_args()); + }; + } + protected function parseClassComposer($class) + { + return str_contains($class, '@') ? explode('@', $class) : array($class, 'compose'); + } + public function callComposer(View $view) + { + $this->events->fire('composing: ' . $view->getName(), array($view)); + } + public function startSection($section, $content = '') + { + if ($content === '') { + ob_start() and $this->sectionStack[] = $section; + } else { + $this->extendSection($section, $content); + } + } + public function inject($section, $content) + { + return $this->startSection($section, $content); + } + public function yieldSection() + { + return $this->yieldContent($this->stopSection()); + } + public function stopSection($overwrite = false) + { + $last = array_pop($this->sectionStack); + if ($overwrite) { + $this->sections[$last] = ob_get_clean(); + } else { + $this->extendSection($last, ob_get_clean()); + } + return $last; + } + protected function extendSection($section, $content) + { + if (isset($this->sections[$section])) { + $content = str_replace('@parent', $content, $this->sections[$section]); + $this->sections[$section] = $content; + } else { + $this->sections[$section] = $content; + } + } + public function yieldContent($section) + { + return isset($this->sections[$section]) ? $this->sections[$section] : ''; + } + public function flushSections() + { + $this->sections = array(); + $this->sectionStack = array(); + } + public function incrementRender() + { + $this->renderCount++; + } + public function decrementRender() + { + $this->renderCount--; + } + public function doneRendering() + { + return $this->renderCount == 0; + } + public function addLocation($location) + { + $this->finder->addLocation($location); + } + public function addNamespace($namespace, $hints) + { + $this->finder->addNamespace($namespace, $hints); + } + public function addExtension($extension, $engine, $resolver = null) + { + $this->finder->addExtension($extension); + if (isset($resolver)) { + $this->engines->register($engine, $resolver); + } + unset($this->extensions[$engine]); + $this->extensions = array_merge(array($extension => $engine), $this->extensions); + } + public function getExtensions() + { + return $this->extensions; + } + public function getEngineResolver() + { + return $this->engines; + } + public function getFinder() + { + return $this->finder; + } + public function getDispatcher() + { + return $this->events; + } + public function getContainer() + { + return $this->container; + } + public function setContainer(Container $container) + { + $this->container = $container; + } + public function getShared() + { + return $this->shared; + } + public function getSections() + { + return $this->sections; + } + public function getNames() + { + return $this->names; + } +} +namespace Illuminate\Support\Contracts; + +interface MessageProviderInterface +{ + public function getMessageBag(); +} +namespace Illuminate\Support; + +use Countable; +use Illuminate\Support\Contracts\ArrayableInterface; +use Illuminate\Support\Contracts\JsonableInterface; +use Illuminate\Support\Contracts\MessageProviderInterface; +class MessageBag implements ArrayableInterface, Countable, JsonableInterface, MessageProviderInterface +{ + protected $messages = array(); + protected $format = ':message'; + public function __construct(array $messages = array()) + { + foreach ($messages as $key => $value) { + $this->messages[$key] = (array) $value; + } + } + public function add($key, $message) + { + if ($this->isUnique($key, $message)) { + $this->messages[$key][] = $message; + } + return $this; + } + public function merge(array $messages) + { + $this->messages = array_merge_recursive($this->messages, $messages); + return $this; + } + protected function isUnique($key, $message) + { + $messages = (array) $this->messages; + return !isset($messages[$key]) or !in_array($message, $messages[$key]); + } + public function has($key = null) + { + return $this->first($key) !== ''; + } + public function first($key = null, $format = null) + { + $messages = is_null($key) ? $this->all($format) : $this->get($key, $format); + return count($messages) > 0 ? $messages[0] : ''; + } + public function get($key, $format = null) + { + $format = $this->checkFormat($format); + if (array_key_exists($key, $this->messages)) { + return $this->transform($this->messages[$key], $format, $key); + } + return array(); + } + public function all($format = null) + { + $format = $this->checkFormat($format); + $all = array(); + foreach ($this->messages as $key => $messages) { + $all = array_merge($all, $this->transform($messages, $format, $key)); + } + return $all; + } + protected function transform($messages, $format, $messageKey) + { + $messages = (array) $messages; + foreach ($messages as $key => &$message) { + $replace = array(':message', ':key'); + $message = str_replace($replace, array($message, $messageKey), $format); + } + return $messages; + } + protected function checkFormat($format) + { + return $format === null ? $this->format : $format; + } + public function getMessages() + { + return $this->messages; + } + public function getMessageBag() + { + return $this; + } + public function getFormat() + { + return $this->format; + } + public function setFormat($format = ':message') + { + $this->format = $format; + } + public function any() + { + return $this->count() > 0; + } + public function count() + { + return count($this->messages); + } + public function toArray() + { + return $this->getMessages(); + } + public function toJson($options = 0) + { + return json_encode($this->toArray(), $options); + } + public function __toString() + { + return $this->toJson(); + } +} +namespace Symfony\Component\Routing; + +use Symfony\Component\HttpFoundation\Request; +class RequestContext +{ + private $baseUrl; + private $pathInfo; + private $method; + private $host; + private $scheme; + private $httpPort; + private $httpsPort; + private $queryString; + private $parameters = array(); + public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443, $path = '/', $queryString = '') + { + $this->baseUrl = $baseUrl; + $this->method = strtoupper($method); + $this->host = $host; + $this->scheme = strtolower($scheme); + $this->httpPort = $httpPort; + $this->httpsPort = $httpsPort; + $this->pathInfo = $path; + $this->queryString = $queryString; + } + public function fromRequest(Request $request) + { + $this->setBaseUrl($request->getBaseUrl()); + $this->setPathInfo($request->getPathInfo()); + $this->setMethod($request->getMethod()); + $this->setHost($request->getHost()); + $this->setScheme($request->getScheme()); + $this->setHttpPort($request->isSecure() ? $this->httpPort : $request->getPort()); + $this->setHttpsPort($request->isSecure() ? $request->getPort() : $this->httpsPort); + $this->setQueryString($request->server->get('QUERY_STRING')); + } + public function getBaseUrl() + { + return $this->baseUrl; + } + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + } + public function getPathInfo() + { + return $this->pathInfo; + } + public function setPathInfo($pathInfo) + { + $this->pathInfo = $pathInfo; + } + public function getMethod() + { + return $this->method; + } + public function setMethod($method) + { + $this->method = strtoupper($method); + } + public function getHost() + { + return $this->host; + } + public function setHost($host) + { + $this->host = $host; + } + public function getScheme() + { + return $this->scheme; + } + public function setScheme($scheme) + { + $this->scheme = strtolower($scheme); + } + public function getHttpPort() + { + return $this->httpPort; + } + public function setHttpPort($httpPort) + { + $this->httpPort = $httpPort; + } + public function getHttpsPort() + { + return $this->httpsPort; + } + public function setHttpsPort($httpsPort) + { + $this->httpsPort = $httpsPort; + } + public function getQueryString() + { + return $this->queryString; + } + public function setQueryString($queryString) + { + $this->queryString = $queryString; + } + public function getParameters() + { + return $this->parameters; + } + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + return $this; + } + public function getParameter($name) + { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + public function hasParameter($name) + { + return array_key_exists($name, $this->parameters); + } + public function setParameter($name, $parameter) + { + $this->parameters[$name] = $parameter; + } +} +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\Routing\RequestContextAwareInterface; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +interface UrlMatcherInterface extends RequestContextAwareInterface +{ + public function match($pathinfo); +} +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +class UrlMatcher implements UrlMatcherInterface +{ + const REQUIREMENT_MATCH = 0; + const REQUIREMENT_MISMATCH = 1; + const ROUTE_MATCH = 2; + protected $context; + protected $allow = array(); + protected $routes; + public function __construct(RouteCollection $routes, RequestContext $context) + { + $this->routes = $routes; + $this->context = $context; + } + public function setContext(RequestContext $context) + { + $this->context = $context; + } + public function getContext() + { + return $this->context; + } + public function match($pathinfo) + { + $this->allow = array(); + if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) { + return $ret; + } + throw 0 < count($this->allow) ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow))) : new ResourceNotFoundException(); + } + protected function matchCollection($pathinfo, RouteCollection $routes) + { + foreach ($routes as $name => $route) { + $compiledRoute = $route->compile(); + if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) { + continue; + } + if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) { + continue; + } + $hostMatches = array(); + if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { + continue; + } + if ($req = $route->getRequirement('_method')) { + if ('HEAD' === ($method = $this->context->getMethod())) { + $method = 'GET'; + } + if (!in_array($method, $req = explode('|', strtoupper($req)))) { + $this->allow = array_merge($this->allow, $req); + continue; + } + } + $status = $this->handleRouteRequirements($pathinfo, $name, $route); + if (self::ROUTE_MATCH === $status[0]) { + return $status[1]; + } + if (self::REQUIREMENT_MISMATCH === $status[0]) { + continue; + } + return $this->getAttributes($route, $name, array_replace($matches, $hostMatches)); + } + } + protected function getAttributes(Route $route, $name, array $attributes) + { + $attributes['_route'] = $name; + return $this->mergeDefaults($attributes, $route->getDefaults()); + } + protected function handleRouteRequirements($pathinfo, $name, Route $route) + { + $scheme = $route->getRequirement('_scheme'); + $status = $scheme && $scheme !== $this->context->getScheme() ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH; + return array($status, null); + } + protected function mergeDefaults($params, $defaults) + { + foreach ($params as $key => $value) { + if (!is_int($key)) { + $defaults[$key] = $value; + } + } + return $defaults; + } +} +namespace Symfony\Component\Routing; + +interface RequestContextAwareInterface +{ + public function setContext(RequestContext $context); + public function getContext(); +} +namespace Symfony\Component\Routing; + +interface RouteCompilerInterface +{ + public static function compile(Route $route); +} +namespace Symfony\Component\Routing; + +class RouteCompiler implements RouteCompilerInterface +{ + const REGEX_DELIMITER = '#'; + const SEPARATORS = '/,;.:-_~+*=@|'; + public static function compile(Route $route) + { + $staticPrefix = null; + $hostVariables = array(); + $pathVariables = array(); + $variables = array(); + $tokens = array(); + $regex = null; + $hostRegex = null; + $hostTokens = array(); + if ('' !== ($host = $route->getHost())) { + $result = self::compilePattern($route, $host, true); + $hostVariables = $result['variables']; + $variables = array_merge($variables, $hostVariables); + $hostTokens = $result['tokens']; + $hostRegex = $result['regex']; + } + $path = $route->getPath(); + $result = self::compilePattern($route, $path, false); + $staticPrefix = $result['staticPrefix']; + $pathVariables = $result['variables']; + $variables = array_merge($variables, $pathVariables); + $tokens = $result['tokens']; + $regex = $result['regex']; + return new CompiledRoute($staticPrefix, $regex, $tokens, $pathVariables, $hostRegex, $hostTokens, $hostVariables, array_unique($variables)); + } + private static function compilePattern(Route $route, $pattern, $isHost) + { + $tokens = array(); + $variables = array(); + $matches = array(); + $pos = 0; + $defaultSeparator = $isHost ? '.' : '/'; + preg_match_all('#\\{\\w+\\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + foreach ($matches as $match) { + $varName = substr($match[0][0], 1, -1); + $precedingText = substr($pattern, $pos, $match[0][1] - $pos); + $pos = $match[0][1] + strlen($match[0][0]); + $precedingChar = strlen($precedingText) > 0 ? substr($precedingText, -1) : ''; + $isSeparator = '' !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar); + if (is_numeric($varName)) { + throw new \DomainException(sprintf('Variable name "%s" cannot be numeric in route pattern "%s". Please use a different name.', $varName, $pattern)); + } + if (in_array($varName, $variables)) { + throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName)); + } + if ($isSeparator && strlen($precedingText) > 1) { + $tokens[] = array('text', substr($precedingText, 0, -1)); + } elseif (!$isSeparator && strlen($precedingText) > 0) { + $tokens[] = array('text', $precedingText); + } + $regexp = $route->getRequirement($varName); + if (null === $regexp) { + $followingPattern = (string) substr($pattern, $pos); + $nextSeparator = self::findNextSeparator($followingPattern); + $regexp = sprintf('[^%s%s]+', preg_quote($defaultSeparator, self::REGEX_DELIMITER), $defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : ''); + if ('' !== $nextSeparator && !preg_match('#^\\{\\w+\\}#', $followingPattern) || '' === $followingPattern) { + $regexp .= '+'; + } + } + $tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName); + $variables[] = $varName; + } + if ($pos < strlen($pattern)) { + $tokens[] = array('text', substr($pattern, $pos)); + } + $firstOptional = PHP_INT_MAX; + if (!$isHost) { + for ($i = count($tokens) - 1; $i >= 0; $i--) { + $token = $tokens[$i]; + if ('variable' === $token[0] && $route->hasDefault($token[3])) { + $firstOptional = $i; + } else { + break; + } + } + } + $regexp = ''; + for ($i = 0, $nbToken = count($tokens); $i < $nbToken; $i++) { + $regexp .= self::computeRegexp($tokens, $i, $firstOptional); + } + return array('staticPrefix' => 'text' === $tokens[0][0] ? $tokens[0][1] : '', 'regex' => self::REGEX_DELIMITER . '^' . $regexp . '$' . self::REGEX_DELIMITER . 's', 'tokens' => array_reverse($tokens), 'variables' => $variables); + } + private static function findNextSeparator($pattern) + { + if ('' == $pattern) { + return ''; + } + $pattern = preg_replace('#\\{\\w+\\}#', '', $pattern); + return isset($pattern[0]) && false !== strpos(static::SEPARATORS, $pattern[0]) ? $pattern[0] : ''; + } + private static function computeRegexp(array $tokens, $index, $firstOptional) + { + $token = $tokens[$index]; + if ('text' === $token[0]) { + return preg_quote($token[1], self::REGEX_DELIMITER); + } else { + if (0 === $index && 0 === $firstOptional) { + return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); + } else { + $regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); + if ($index >= $firstOptional) { + $regexp = "(?:{$regexp}"; + $nbTokens = count($tokens); + if ($nbTokens - 1 == $index) { + $regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0)); + } + } + return $regexp; + } + } + } +} +namespace Symfony\Component\Routing; + +class CompiledRoute +{ + private $variables; + private $tokens; + private $staticPrefix; + private $regex; + private $pathVariables; + private $hostVariables; + private $hostRegex; + private $hostTokens; + public function __construct($staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array()) + { + $this->staticPrefix = (string) $staticPrefix; + $this->regex = $regex; + $this->tokens = $tokens; + $this->pathVariables = $pathVariables; + $this->hostRegex = $hostRegex; + $this->hostTokens = $hostTokens; + $this->hostVariables = $hostVariables; + $this->variables = $variables; + } + public function getStaticPrefix() + { + return $this->staticPrefix; + } + public function getRegex() + { + return $this->regex; + } + public function getHostRegex() + { + return $this->hostRegex; + } + public function getTokens() + { + return $this->tokens; + } + public function getHostTokens() + { + return $this->hostTokens; + } + public function getVariables() + { + return $this->variables; + } + public function getPathVariables() + { + return $this->pathVariables; + } + public function getHostVariables() + { + return $this->hostVariables; + } +} +namespace Illuminate\Support\Facades; + +class View extends Facade +{ + protected static function getFacadeAccessor() + { + return 'view'; + } +} +namespace Illuminate\Support\Contracts; + +interface RenderableInterface +{ + public function render(); +} +namespace Illuminate\View; + +use ArrayAccess; +use Illuminate\View\Engines\EngineInterface; +use Illuminate\Support\Contracts\ArrayableInterface as Arrayable; +use Illuminate\Support\Contracts\RenderableInterface as Renderable; +class View implements ArrayAccess, Renderable +{ + protected $environment; + protected $engine; + protected $view; + protected $data; + protected $path; + public function __construct(Environment $environment, EngineInterface $engine, $view, $path, $data = array()) + { + $this->view = $view; + $this->path = $path; + $this->engine = $engine; + $this->environment = $environment; + $this->data = $data instanceof Arrayable ? $data->toArray() : (array) $data; + } + public function render() + { + $env = $this->environment; + $env->incrementRender(); + $env->callComposer($this); + $contents = trim($this->getContents()); + $env->decrementRender(); + if ($env->doneRendering()) { + $env->flushSections(); + } + return $contents; + } + protected function getContents() + { + return $this->engine->get($this->path, $this->gatherData()); + } + protected function gatherData() + { + $data = array_merge($this->environment->getShared(), $this->data); + foreach ($data as $key => $value) { + if ($value instanceof Renderable) { + $data[$key] = $value->render(); + } + } + return $data; + } + public function with($key, $value = null) + { + if (is_array($key)) { + $this->data = array_merge($this->data, $key); + } else { + $this->data[$key] = $value; + } + return $this; + } + public function nest($key, $view, array $data = array()) + { + return $this->with($key, $this->environment->make($view, $data)); + } + public function getEnvironment() + { + return $this->environment; + } + public function getEngine() + { + return $this->engine; + } + public function getName() + { + return $this->view; + } + public function getData() + { + return $this->data; + } + public function getPath() + { + return $this->path; + } + public function setPath($path) + { + $this->path = $path; + } + public function offsetExists($key) + { + return array_key_exists($key, $this->data); + } + public function offsetGet($key) + { + return $this->data[$key]; + } + public function offsetSet($key, $value) + { + $this->with($key, $value); + } + public function offsetUnset($key) + { + unset($this->data[$key]); + } + public function __get($key) + { + return $this->data[$key]; + } + public function __set($key, $value) + { + $this->with($key, $value); + } + public function __isset($key) + { + return isset($this->data[$key]); + } + public function __unset($key) + { + unset($this->data[$key]); + } + public function __toString() + { + return $this->render(); + } +} +namespace Illuminate\View\Engines; + +interface EngineInterface +{ + public function get($path, array $data = array()); +} +namespace Illuminate\View\Engines; + +use Illuminate\View\Exception; +use Illuminate\View\Environment; +class PhpEngine implements EngineInterface +{ + public function get($path, array $data = array()) + { + return $this->evaluatePath($path, $data); + } + protected function evaluatePath($__path, $__data) + { + ob_start(); + extract($__data); + try { + include $__path; + } catch (\Exception $e) { + $this->handleViewException($e); + } + return ob_get_clean(); + } + protected function handleViewException($e) + { + ob_get_clean(); + throw $e; + } +} +namespace Symfony\Component\HttpFoundation; + +class Response +{ + public $headers; + protected $content; + protected $version; + protected $statusCode; + protected $statusText; + protected $charset; + public static $statusTexts = array(100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 208 => 'Already Reported', 226 => 'IM Used', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Reserved', 307 => 'Temporary Redirect', 308 => 'Permanent Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Reserved for WebDAV advanced collections expired proposal', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates (Experimental)', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 510 => 'Not Extended', 511 => 'Network Authentication Required'); + public function __construct($content = '', $status = 200, $headers = array()) + { + $this->headers = new ResponseHeaderBag($headers); + $this->setContent($content); + $this->setStatusCode($status); + $this->setProtocolVersion('1.0'); + if (!$this->headers->has('Date')) { + $this->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); + } + } + public static function create($content = '', $status = 200, $headers = array()) + { + return new static($content, $status, $headers); + } + public function __toString() + { + return sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText) . ' +' . $this->headers . ' +' . $this->getContent(); + } + public function __clone() + { + $this->headers = clone $this->headers; + } + public function prepare(Request $request) + { + $headers = $this->headers; + if ($this->isInformational() || in_array($this->statusCode, array(204, 304))) { + $this->setContent(null); + } + if (!$headers->has('Content-Type')) { + $format = $request->getRequestFormat(); + if (null !== $format && ($mimeType = $request->getMimeType($format))) { + $headers->set('Content-Type', $mimeType); + } + } + $charset = $this->charset ?: 'UTF-8'; + if (!$headers->has('Content-Type')) { + $headers->set('Content-Type', 'text/html; charset=' . $charset); + } elseif (0 === strpos($headers->get('Content-Type'), 'text/') && false === strpos($headers->get('Content-Type'), 'charset')) { + $headers->set('Content-Type', $headers->get('Content-Type') . '; charset=' . $charset); + } + if ($headers->has('Transfer-Encoding')) { + $headers->remove('Content-Length'); + } + if ($request->isMethod('HEAD')) { + $length = $headers->get('Content-Length'); + $this->setContent(null); + if ($length) { + $headers->set('Content-Length', $length); + } + } + if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) { + $this->setProtocolVersion('1.1'); + } + if ('1.0' == $this->getProtocolVersion() && 'no-cache' == $this->headers->get('Cache-Control')) { + $this->headers->set('pragma', 'no-cache'); + $this->headers->set('expires', -1); + } + $this->ensureIEOverSSLCompatibility($request); + return $this; + } + public function sendHeaders() + { + if (headers_sent()) { + return $this; + } + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); + foreach ($this->headers->allPreserveCase() as $name => $values) { + foreach ($values as $value) { + header($name . ': ' . $value, false); + } + } + foreach ($this->headers->getCookies() as $cookie) { + setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } + return $this; + } + public function sendContent() + { + echo $this->content; + return $this; + } + public function send() + { + $this->sendHeaders(); + $this->sendContent(); + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } elseif ('cli' !== PHP_SAPI) { + $previous = null; + $obStatus = ob_get_status(1); + while (($level = ob_get_level()) > 0 && $level !== $previous) { + $previous = $level; + if ($obStatus[$level - 1] && isset($obStatus[$level - 1]['del']) && $obStatus[$level - 1]['del']) { + ob_end_flush(); + } + } + flush(); + } + return $this; + } + public function setContent($content) + { + if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) { + throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content))); + } + $this->content = (string) $content; + return $this; + } + public function getContent() + { + return $this->content; + } + public function setProtocolVersion($version) + { + $this->version = $version; + return $this; + } + public function getProtocolVersion() + { + return $this->version; + } + public function setStatusCode($code, $text = null) + { + $this->statusCode = $code = (int) $code; + if ($this->isInvalid()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); + } + if (null === $text) { + $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : ''; + return $this; + } + if (false === $text) { + $this->statusText = ''; + return $this; + } + $this->statusText = $text; + return $this; + } + public function getStatusCode() + { + return $this->statusCode; + } + public function setCharset($charset) + { + $this->charset = $charset; + return $this; + } + public function getCharset() + { + return $this->charset; + } + public function isCacheable() + { + if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { + return false; + } + if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) { + return false; + } + return $this->isValidateable() || $this->isFresh(); + } + public function isFresh() + { + return $this->getTtl() > 0; + } + public function isValidateable() + { + return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); + } + public function setPrivate() + { + $this->headers->removeCacheControlDirective('public'); + $this->headers->addCacheControlDirective('private'); + return $this; + } + public function setPublic() + { + $this->headers->addCacheControlDirective('public'); + $this->headers->removeCacheControlDirective('private'); + return $this; + } + public function mustRevalidate() + { + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('proxy-revalidate'); + } + public function getDate() + { + return $this->headers->getDate('Date', new \DateTime()); + } + public function setDate(\DateTime $date) + { + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Date', $date->format('D, d M Y H:i:s') . ' GMT'); + return $this; + } + public function getAge() + { + if (null !== ($age = $this->headers->get('Age'))) { + return (int) $age; + } + return max(time() - $this->getDate()->format('U'), 0); + } + public function expire() + { + if ($this->isFresh()) { + $this->headers->set('Age', $this->getMaxAge()); + } + return $this; + } + public function getExpires() + { + try { + return $this->headers->getDate('Expires'); + } catch (\RuntimeException $e) { + return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000'); + } + } + public function setExpires(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Expires'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Expires', $date->format('D, d M Y H:i:s') . ' GMT'); + } + return $this; + } + public function getMaxAge() + { + if ($this->headers->hasCacheControlDirective('s-maxage')) { + return (int) $this->headers->getCacheControlDirective('s-maxage'); + } + if ($this->headers->hasCacheControlDirective('max-age')) { + return (int) $this->headers->getCacheControlDirective('max-age'); + } + if (null !== $this->getExpires()) { + return $this->getExpires()->format('U') - $this->getDate()->format('U'); + } + return null; + } + public function setMaxAge($value) + { + $this->headers->addCacheControlDirective('max-age', $value); + return $this; + } + public function setSharedMaxAge($value) + { + $this->setPublic(); + $this->headers->addCacheControlDirective('s-maxage', $value); + return $this; + } + public function getTtl() + { + if (null !== ($maxAge = $this->getMaxAge())) { + return $maxAge - $this->getAge(); + } + return null; + } + public function setTtl($seconds) + { + $this->setSharedMaxAge($this->getAge() + $seconds); + return $this; + } + public function setClientTtl($seconds) + { + $this->setMaxAge($this->getAge() + $seconds); + return $this; + } + public function getLastModified() + { + return $this->headers->getDate('Last-Modified'); + } + public function setLastModified(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Last-Modified'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s') . ' GMT'); + } + return $this; + } + public function getEtag() + { + return $this->headers->get('ETag'); + } + public function setEtag($etag = null, $weak = false) + { + if (null === $etag) { + $this->headers->remove('Etag'); + } else { + if (0 !== strpos($etag, '"')) { + $etag = '"' . $etag . '"'; + } + $this->headers->set('ETag', (true === $weak ? 'W/' : '') . $etag); + } + return $this; + } + public function setCache(array $options) + { + if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) { + throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff)))); + } + if (isset($options['etag'])) { + $this->setEtag($options['etag']); + } + if (isset($options['last_modified'])) { + $this->setLastModified($options['last_modified']); + } + if (isset($options['max_age'])) { + $this->setMaxAge($options['max_age']); + } + if (isset($options['s_maxage'])) { + $this->setSharedMaxAge($options['s_maxage']); + } + if (isset($options['public'])) { + if ($options['public']) { + $this->setPublic(); + } else { + $this->setPrivate(); + } + } + if (isset($options['private'])) { + if ($options['private']) { + $this->setPrivate(); + } else { + $this->setPublic(); + } + } + return $this; + } + public function setNotModified() + { + $this->setStatusCode(304); + $this->setContent(null); + foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) { + $this->headers->remove($header); + } + return $this; + } + public function hasVary() + { + return null !== $this->headers->get('Vary'); + } + public function getVary() + { + if (!($vary = $this->headers->get('Vary'))) { + return array(); + } + return is_array($vary) ? $vary : preg_split('/[\\s,]+/', $vary); + } + public function setVary($headers, $replace = true) + { + $this->headers->set('Vary', $headers, $replace); + return $this; + } + public function isNotModified(Request $request) + { + if (!$request->isMethodSafe()) { + return false; + } + $lastModified = $request->headers->get('If-Modified-Since'); + $notModified = false; + if ($etags = $request->getEtags()) { + $notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified); + } elseif ($lastModified) { + $notModified = $lastModified == $this->headers->get('Last-Modified'); + } + if ($notModified) { + $this->setNotModified(); + } + return $notModified; + } + public function isInvalid() + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + public function isInformational() + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + public function isSuccessful() + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + public function isRedirection() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + public function isOk() + { + return 200 === $this->statusCode; + } + public function isForbidden() + { + return 403 === $this->statusCode; + } + public function isNotFound() + { + return 404 === $this->statusCode; + } + public function isRedirect($location = null) + { + return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location')); + } + public function isEmpty() + { + return in_array($this->statusCode, array(201, 204, 304)); + } + protected function ensureIEOverSSLCompatibility(Request $request) + { + if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) == 1 && true === $request->isSecure()) { + if (intval(preg_replace('/(MSIE )(.*?);/', '$2', $match[0])) < 9) { + $this->headers->remove('Cache-Control'); + } + } + } +} +namespace Illuminate\Http; + +use Symfony\Component\HttpFoundation\Cookie; +use Illuminate\Support\Contracts\JsonableInterface; +use Illuminate\Support\Contracts\RenderableInterface; +class Response extends \Symfony\Component\HttpFoundation\Response +{ + public $original; + public function header($key, $value, $replace = true) + { + $this->headers->set($key, $value, $replace); + return $this; + } + public function withCookie(Cookie $cookie) + { + $this->headers->setCookie($cookie); + return $this; + } + public function setContent($content) + { + $this->original = $content; + if ($this->shouldBeJson($content)) { + $this->headers->set('Content-Type', 'application/json'); + $content = $this->morphToJson($content); + } elseif ($content instanceof RenderableInterface) { + $content = $content->render(); + } + return parent::setContent($content); + } + protected function morphToJson($content) + { + if ($content instanceof JsonableInterface) { + return $content->toJson(); + } + return json_encode($content); + } + protected function shouldBeJson($content) + { + return $content instanceof JsonableInterface or is_array($content); + } + public function getOriginalContent() + { + return $this->original; + } +} +namespace Symfony\Component\HttpFoundation; + +class ResponseHeaderBag extends HeaderBag +{ + const COOKIES_FLAT = 'flat'; + const COOKIES_ARRAY = 'array'; + const DISPOSITION_ATTACHMENT = 'attachment'; + const DISPOSITION_INLINE = 'inline'; + protected $computedCacheControl = array(); + protected $cookies = array(); + protected $headerNames = array(); + public function __construct(array $headers = array()) + { + parent::__construct($headers); + if (!isset($this->headers['cache-control'])) { + $this->set('Cache-Control', ''); + } + } + public function __toString() + { + $cookies = ''; + foreach ($this->getCookies() as $cookie) { + $cookies .= 'Set-Cookie: ' . $cookie . ' +'; + } + ksort($this->headerNames); + return parent::__toString() . $cookies; + } + public function allPreserveCase() + { + return array_combine($this->headerNames, $this->headers); + } + public function replace(array $headers = array()) + { + $this->headerNames = array(); + parent::replace($headers); + if (!isset($this->headers['cache-control'])) { + $this->set('Cache-Control', ''); + } + } + public function set($key, $values, $replace = true) + { + parent::set($key, $values, $replace); + $uniqueKey = strtr(strtolower($key), '_', '-'); + $this->headerNames[$uniqueKey] = $key; + if (in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'))) { + $computed = $this->computeCacheControlValue(); + $this->headers['cache-control'] = array($computed); + $this->headerNames['cache-control'] = 'Cache-Control'; + $this->computedCacheControl = $this->parseCacheControl($computed); + } + } + public function remove($key) + { + parent::remove($key); + $uniqueKey = strtr(strtolower($key), '_', '-'); + unset($this->headerNames[$uniqueKey]); + if ('cache-control' === $uniqueKey) { + $this->computedCacheControl = array(); + } + } + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl); + } + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; + } + public function setCookie(Cookie $cookie) + { + $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; + } + public function removeCookie($name, $path = '/', $domain = null) + { + if (null === $path) { + $path = '/'; + } + unset($this->cookies[$domain][$path][$name]); + if (empty($this->cookies[$domain][$path])) { + unset($this->cookies[$domain][$path]); + if (empty($this->cookies[$domain])) { + unset($this->cookies[$domain]); + } + } + } + public function getCookies($format = self::COOKIES_FLAT) + { + if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) { + throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY)))); + } + if (self::COOKIES_ARRAY === $format) { + return $this->cookies; + } + $flattenedCookies = array(); + foreach ($this->cookies as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + return $flattenedCookies; + } + public function clearCookie($name, $path = '/', $domain = null) + { + $this->setCookie(new Cookie($name, null, 1, $path, $domain)); + } + public function makeDisposition($disposition, $filename, $filenameFallback = '') + { + if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) { + throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); + } + if ('' == $filenameFallback) { + $filenameFallback = $filename; + } + if (!preg_match('/^[\\x20-\\x7e]*$/', $filenameFallback)) { + throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); + } + if (false !== strpos($filenameFallback, '%')) { + throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); + } + if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) { + throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); + } + $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback)); + if ($filename !== $filenameFallback) { + $output .= sprintf('; filename*=utf-8\'\'%s', rawurlencode($filename)); + } + return $output; + } + protected function computeCacheControlValue() + { + if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { + return 'no-cache'; + } + if (!$this->cacheControl) { + return 'private, must-revalidate'; + } + $header = $this->getCacheControlHeader(); + if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) { + return $header; + } + if (!isset($this->cacheControl['s-maxage'])) { + return $header . ', private'; + } + return $header; + } +} +namespace Symfony\Component\HttpFoundation; + +class Cookie +{ + protected $name; + protected $value; + protected $domain; + protected $expire; + protected $path; + protected $secure; + protected $httpOnly; + public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true) + { + if (preg_match('/[=,; + ]/', $name)) { + throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name)); + } + if (empty($name)) { + throw new \InvalidArgumentException('The cookie name cannot be empty.'); + } + if ($expire instanceof \DateTime) { + $expire = $expire->format('U'); + } elseif (!is_numeric($expire)) { + $expire = strtotime($expire); + if (false === $expire || -1 === $expire) { + throw new \InvalidArgumentException('The cookie expiration time is not valid.'); + } + } + $this->name = $name; + $this->value = $value; + $this->domain = $domain; + $this->expire = $expire; + $this->path = empty($path) ? '/' : $path; + $this->secure = (bool) $secure; + $this->httpOnly = (bool) $httpOnly; + } + public function __toString() + { + $str = urlencode($this->getName()) . '='; + if ('' === (string) $this->getValue()) { + $str .= 'deleted; expires=' . gmdate('D, d-M-Y H:i:s T', time() - 31536001); + } else { + $str .= urlencode($this->getValue()); + if ($this->getExpiresTime() !== 0) { + $str .= '; expires=' . gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()); + } + } + if ($this->path) { + $str .= '; path=' . $this->path; + } + if ($this->getDomain()) { + $str .= '; domain=' . $this->getDomain(); + } + if (true === $this->isSecure()) { + $str .= '; secure'; + } + if (true === $this->isHttpOnly()) { + $str .= '; httponly'; + } + return $str; + } + public function getName() + { + return $this->name; + } + public function getValue() + { + return $this->value; + } + public function getDomain() + { + return $this->domain; + } + public function getExpiresTime() + { + return $this->expire; + } + public function getPath() + { + return $this->path; + } + public function isSecure() + { + return $this->secure; + } + public function isHttpOnly() + { + return $this->httpOnly; + } + public function isCleared() + { + return $this->expire < time(); + } +} +namespace Whoops; + +use Whoops\Handler\HandlerInterface; +use Whoops\Handler\Handler; +use Whoops\Handler\CallbackHandler; +use Whoops\Exception\Inspector; +use Whoops\Exception\ErrorException; +use InvalidArgumentException; +use Exception; +class Run +{ + const EXCEPTION_HANDLER = 'handleException'; + const ERROR_HANDLER = 'handleError'; + const SHUTDOWN_HANDLER = 'handleShutdown'; + protected $isRegistered; + protected $allowQuit = true; + protected $sendOutput = true; + protected $handlerStack = array(); + public function pushHandler($handler) + { + if (is_callable($handler)) { + $handler = new CallbackHandler($handler); + } + if (!$handler instanceof HandlerInterface) { + throw new InvalidArgumentException('Argument to ' . __METHOD__ . ' must be a callable, or instance of' . 'Whoops\\Handler\\HandlerInterface'); + } + $this->handlerStack[] = $handler; + return $this; + } + public function popHandler() + { + return array_pop($this->handlerStack); + } + public function getHandlers() + { + return $this->handlerStack; + } + public function clearHandlers() + { + $this->handlerStack = array(); + return $this; + } + protected function getInspector(Exception $exception) + { + return new Inspector($exception); + } + public function register() + { + if (!$this->isRegistered) { + set_error_handler(array($this, self::ERROR_HANDLER)); + set_exception_handler(array($this, self::EXCEPTION_HANDLER)); + register_shutdown_function(array($this, self::SHUTDOWN_HANDLER)); + $this->isRegistered = true; + } + return $this; + } + public function unregister() + { + if ($this->isRegistered) { + restore_exception_handler(); + restore_error_handler(); + $this->isRegistered = false; + } + return $this; + } + public function allowQuit($exit = null) + { + if (func_num_args() == 0) { + return $this->allowQuit; + } + return $this->allowQuit = (bool) $exit; + } + public function writeToOutput($send = null) + { + if (func_num_args() == 0) { + return $this->sendOutput; + } + return $this->sendOutput = (bool) $send; + } + public function handleException(Exception $exception) + { + $inspector = $this->getInspector($exception); + ob_start(); + for ($i = count($this->handlerStack) - 1; $i >= 0; $i--) { + $handler = $this->handlerStack[$i]; + $handler->setRun($this); + $handler->setInspector($inspector); + $handler->setException($exception); + $handlerResponse = $handler->handle($exception); + if (in_array($handlerResponse, array(Handler::LAST_HANDLER, Handler::QUIT))) { + break; + } + } + $output = ob_get_clean(); + if ($this->allowQuit()) { + echo $output; + die; + } else { + if ($this->writeToOutput()) { + echo $output; + } + return $output; + } + } + public function handleError($level, $message, $file = null, $line = null) + { + if ($level & error_reporting()) { + $this->handleException(new ErrorException($message, $level, 0, $file, $line)); + } + } + public function handleShutdown() + { + if ($error = error_get_last()) { + $this->handleError($error['type'], $error['message'], $error['file'], $error['line']); + } + } +} +namespace Whoops\Handler; + +use Whoops\Exception\Inspector; +use Whoops\Run; +use Exception; +interface HandlerInterface +{ + public function handle(); + public function setRun(Run $run); + public function setException(Exception $exception); + public function setInspector(Inspector $inspector); +} +namespace Whoops\Handler; + +use Whoops\Handler\HandlerInterface; +use Whoops\Exception\Inspector; +use Whoops\Run; +use Exception; +abstract class Handler implements HandlerInterface +{ + const DONE = 16; + const LAST_HANDLER = 32; + const QUIT = 48; + private $run; + private $inspector; + private $exception; + public function setRun(Run $run) + { + $this->run = $run; + } + protected function getRun() + { + return $this->run; + } + public function setInspector(Inspector $inspector) + { + $this->inspector = $inspector; + } + protected function getInspector() + { + return $this->inspector; + } + public function setException(Exception $exception) + { + $this->exception = $exception; + } + protected function getException() + { + return $this->exception; + } +} +namespace Whoops\Handler; + +use Whoops\Handler\Handler; +use InvalidArgumentException; +class PrettyPageHandler extends Handler +{ + private $resourcesPath; + private $extraTables = array(); + private $pageTitle = 'Whoops! There was an error.'; + protected $editor; + protected $editors = array('sublime' => 'subl://open?url=file://%file&line=%line', 'textmate' => 'txmt://open?url=file://%file&line=%line', 'emacs' => 'emacs://open?url=file://%file&line=%line', 'macvim' => 'mvim://open/?url=file://%file&line=%line'); + public function __construct() + { + if (extension_loaded('xdebug')) { + $this->editors['xdebug'] = function ($file, $line) { + return str_replace(array('%f', '%l'), array($file, $line), ini_get('xdebug.file_link_format')); + }; + } + } + public function handle() + { + if (php_sapi_name() === 'cli' && !isset($_ENV['whoops-test'])) { + return Handler::DONE; + } + if (!($resources = $this->getResourcesPath())) { + $resources = 'F:\\Nelson\\My Documents - Personal\\Visual Studio 2010\\Projects\\Poniverse\\spa.pony.fm\\vendor\\filp\\whoops\\src\\Whoops\\Handler' . '/../Resources'; + } + $templateFile = "{$resources}/pretty-template.php"; + $cssFile = "{$resources}/pretty-page.css"; + $inspector = $this->getInspector(); + $frames = $inspector->getFrames(); + $v = (object) array('title' => $this->getPageTitle(), 'name' => explode('\\', $inspector->getExceptionName()), 'message' => $inspector->getException()->getMessage(), 'frames' => $frames, 'hasFrames' => !!count($frames), 'handler' => $this, 'handlers' => $this->getRun()->getHandlers(), 'pageStyle' => file_get_contents($cssFile), 'tables' => array('Server/Request Data' => $_SERVER, 'GET Data' => $_GET, 'POST Data' => $_POST, 'Files' => $_FILES, 'Cookies' => $_COOKIE, 'Session' => isset($_SESSION) ? $_SESSION : array(), 'Environment Variables' => $_ENV)); + $extraTables = array_map(function ($table) { + return $table instanceof \Closure ? $table() : $table; + }, $this->getDataTables()); + $v->tables = array_merge($extraTables, $v->tables); + call_user_func(function () use($templateFile, $v) { + $e = function ($_, $allowLinks = false) { + $escaped = htmlspecialchars($_, ENT_QUOTES, 'UTF-8'); + if ($allowLinks) { + $escaped = preg_replace('@([A-z]+?://([-\\w\\.]+[-\\w])+(:\\d+)?(/([\\w/_\\.#-]*(\\?\\S+)?[^\\.\\s])?)?)@', '$1', $escaped); + } + return $escaped; + }; + $slug = function ($_) { + $_ = str_replace(' ', '-', $_); + $_ = preg_replace('/[^\\w\\d\\-\\_]/i', '', $_); + return strtolower($_); + }; + require $templateFile; + }); + return Handler::QUIT; + } + public function addDataTable($label, array $data) + { + $this->extraTables[$label] = $data; + } + public function addDataTableCallback($label, $callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException('Expecting callback argument to be callable'); + } + $this->extraTables[$label] = function () use($callback) { + try { + $result = call_user_func($callback); + return is_array($result) || $result instanceof \Traversable ? $result : array(); + } catch (\Exception $e) { + return array(); + } + }; + } + public function getDataTables($label = null) + { + if ($label !== null) { + return isset($this->extraTables[$label]) ? $this->extraTables[$label] : array(); + } + return $this->extraTables; + } + public function addEditor($identifier, $resolver) + { + $this->editors[$identifier] = $resolver; + } + public function setEditor($editor) + { + if (!is_callable($editor) && !isset($this->editors[$editor])) { + throw new InvalidArgumentException("Unknown editor identifier: {$editor}. Known editors:" . implode(',', array_keys($this->editors))); + } + $this->editor = $editor; + } + public function getEditorHref($filePath, $line) + { + if ($this->editor === null) { + return false; + } + $editor = $this->editor; + if (is_string($editor)) { + $editor = $this->editors[$editor]; + } + if (is_callable($editor)) { + $editor = call_user_func($editor, $filePath, $line); + } + if (!is_string($editor)) { + throw new InvalidArgumentException(__METHOD__ . ' should always resolve to a string; got something else instead'); + } + $editor = str_replace('%line', rawurlencode($line), $editor); + $editor = str_replace('%file', rawurlencode($filePath), $editor); + return $editor; + } + public function setPageTitle($title) + { + $this->pageTitle = (string) $title; + } + public function getPageTitle() + { + return $this->pageTitle; + } + public function getResourcesPath() + { + return $this->resourcesPath; + } + public function setResourcesPath($resourcesPath) + { + if (!is_dir($resourcesPath)) { + throw new InvalidArgumentException("{$resourcesPath} is not a valid directory"); + } + $this->resourcesPath = $resourcesPath; + } +} +namespace Whoops\Handler; + +use Whoops\Handler\Handler; +class JsonResponseHandler extends Handler +{ + private $returnFrames = false; + private $onlyForAjaxRequests = false; + public function addTraceToOutput($returnFrames = null) + { + if (func_num_args() == 0) { + return $this->returnFrames; + } + $this->returnFrames = (bool) $returnFrames; + } + public function onlyForAjaxRequests($onlyForAjaxRequests = null) + { + if (func_num_args() == 0) { + return $this->onlyForAjaxRequests; + } + $this->onlyForAjaxRequests = (bool) $onlyForAjaxRequests; + } + private function isAjaxRequest() + { + return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; + } + public function handle() + { + if ($this->onlyForAjaxRequests() && !$this->isAjaxRequest()) { + return Handler::DONE; + } + $exception = $this->getException(); + $response = array('error' => array('type' => get_class($exception), 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'line' => $exception->getLine())); + if ($this->addTraceToOutput()) { + $inspector = $this->getInspector(); + $frames = $inspector->getFrames(); + $frameData = array(); + foreach ($frames as $frame) { + $frameData[] = array('file' => $frame->getFile(), 'line' => $frame->getLine(), 'function' => $frame->getFunction(), 'class' => $frame->getClass(), 'args' => $frame->getArgs()); + } + $response['error']['trace'] = $frameData; + } + echo json_encode($response); + return Handler::QUIT; + } +} diff --git a/bootstrap/paths.php b/bootstrap/paths.php new file mode 100644 index 00000000..5a1f640b --- /dev/null +++ b/bootstrap/paths.php @@ -0,0 +1,57 @@ + __DIR__.'/../app', + + /* + |-------------------------------------------------------------------------- + | Public Path + |-------------------------------------------------------------------------- + | + | The public path contains the assets for your web application, such as + | your JavaScript and CSS files, and also contains the primary entry + | point for web requests into these applications from the outside. + | + */ + + 'public' => __DIR__.'/../public', + + /* + |-------------------------------------------------------------------------- + | Base Path + |-------------------------------------------------------------------------- + | + | The base path is the root of the Laravel installation. Most likely you + | will not need to change this value. But, if for some wild reason it + | is necessary you will do so here, just proceed with some caution. + | + */ + + 'base' => __DIR__.'/..', + + /* + |-------------------------------------------------------------------------- + | Storage Path + |-------------------------------------------------------------------------- + | + | The storage path is used by Laravel to store cached Blade views, logs + | and other pieces of information. You may modify the path here when + | you want to change the location of this directory for your apps. + | + */ + + 'storage' => __DIR__.'/../app/storage', + +); diff --git a/bootstrap/start.php b/bootstrap/start.php new file mode 100644 index 00000000..8b882aef --- /dev/null +++ b/bootstrap/start.php @@ -0,0 +1,72 @@ +redirectIfTrailingSlash(); + +/* +|-------------------------------------------------------------------------- +| Detect The Application Environment +|-------------------------------------------------------------------------- +| +| Laravel takes a dead simple approach to your application environments +| so you can just specify a machine name or HTTP host that matches a +| given environment, then we will automatically detect it for you. +| +*/ + +$env = $app->detectEnvironment([ + 'local' => ['dev.spa.pony.fm'] +]); + +/* +|-------------------------------------------------------------------------- +| Bind Paths +|-------------------------------------------------------------------------- +| +| Here we are binding the paths configured in paths.php to the app. You +| should not be changing these here. If you need to change these you +| may do so within the paths.php file and they will be bound here. +| +*/ + +$app->bindInstallPaths(require __DIR__.'/paths.php'); + +/* +|-------------------------------------------------------------------------- +| Load The Application +|-------------------------------------------------------------------------- +| +| Here we will load the Illuminate application. We'll keep this is in a +| separate location so we can isolate the creation of an application +| from the actual running of the application with a given request. +| +*/ + +$framework = $app['path.base'].'/vendor/laravel/framework/src'; + +require $framework.'/Illuminate/Foundation/start.php'; + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..5b223f3c --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "require": { + "laravel/framework": "4.0.*", + "kriswallsmith/assetic": "1.2.*@dev", + "codescale/ffmpeg-php": "2.7.0" + }, + "autoload": { + "classmap": [ + "app/commands", + "app/controllers", + "app/models", + "app/database/migrations", + "app/database/seeds", + "app/tests/TestCase.php" + ] + }, + "scripts": { + "pre-update-cmd": [ + "php artisan clear-compiled" + ], + "post-install-cmd": [ + "php artisan optimize" + ], + "post-update-cmd": [ + "php artisan optimize" + ] + }, + "config": { + "preferred-install": "dist" + }, + "minimum-stability": "dev" +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..9057ac30 --- /dev/null +++ b/composer.lock @@ -0,0 +1,2303 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "c97b33f82a1e14bf632925de72277467", + "packages": [ + { + "name": "cboden/ratchet", + "version": "0.3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/cboden/Ratchet.git", + "reference": "f4ddea5f44bc64c06016acea9da80e5e87830a7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cboden/Ratchet/zipball/f4ddea5f44bc64c06016acea9da80e5e87830a7a", + "reference": "f4ddea5f44bc64c06016acea9da80e5e87830a7a", + "shasum": "" + }, + "require": { + "guzzle/http": ">=3.0,<4.0", + "php": ">=5.3.9", + "react/socket": ">=0.2,<1.0", + "symfony/http-foundation": ">=2.2,<3.0", + "symfony/routing": ">=2.2,<3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Ratchet\\Tests": "tests", + "Ratchet": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "http://res.im", + "role": "Developer" + } + ], + "description": "PHP WebSocket library", + "homepage": "http://socketo.me", + "keywords": [ + "Ratchet", + "WebSockets", + "server", + "sockets" + ], + "time": "2013-05-29 11:51:33" + }, + { + "name": "classpreloader/classpreloader", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/mtdowling/ClassPreloader.git", + "reference": "62c99d52ce2f1b0b8449c61e2d94f48d918222eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mtdowling/ClassPreloader/zipball/62c99d52ce2f1b0b8449c61e2d94f48d918222eb", + "reference": "62c99d52ce2f1b0b8449c61e2d94f48d918222eb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "*", + "php": ">=5.3.3", + "symfony/console": ">2.0", + "symfony/filesystem": ">2.0", + "symfony/finder": ">2.0" + }, + "bin": [ + "classpreloader.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.0-dev" + } + }, + "autoload": { + "psr-0": { + "ClassPreloader": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Helps class loading performance by generating a single PHP file containing all of the autoloaded files for a specific use case", + "keywords": [ + "autoload", + "class", + "preload" + ], + "time": "2013-05-26 16:10:36" + }, + { + "name": "codescale/ffmpeg-php", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/CodeScaleInc/ffmpeg-php.git", + "reference": "2.7.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CodeScaleInc/ffmpeg-php/zipball/2.7.0", + "reference": "2.7.0", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "." + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "New BSD" + ], + "authors": [ + { + "name": "char0n (Vladimír Gorej, CodeScale s.r.o.)", + "email": "gorej@codescale.net", + "homepage": "http://www.codescale.net/", + "role": "Development lead" + } + ], + "description": "PHP wrapper for FFmpeg application", + "homepage": "http://freecode.com/projects/ffmpegphp", + "keywords": [ + "audio", + "ffmpeg", + "video" + ], + "time": "2013-05-05 09:10:04" + }, + { + "name": "doctrine/annotations", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "v1.1.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/v1.1.1", + "reference": "v1.1.1", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2013-04-20 08:30:17" + }, + { + "name": "doctrine/cache", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "45123145f70dd79618963a72a5271b4f389712e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/45123145f70dd79618963a72a5271b4f389712e4", + "reference": "45123145f70dd79618963a72a5271b4f389712e4", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Cache\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan H. Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2013-05-13 02:51:07" + }, + { + "name": "doctrine/collections", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "3db3ab843ff76774bee4679d4cb3a10cffb0a935" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/3db3ab843ff76774bee4679d4cb3a10cffb0a935", + "reference": "3db3ab843ff76774bee4679d4cb3a10cffb0a935", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2013-05-26 05:21:22" + }, + { + "name": "doctrine/common", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "2169b0ce1d253d448c60b7d40bbe4e4b5afe22fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/2169b0ce1d253d448c60b7d40bbe4e4b5afe22fe", + "reference": "2169b0ce1d253d448c60b7d40bbe4e4b5afe22fe", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2013-05-27 19:11:46" + }, + { + "name": "doctrine/dbal", + "version": "2.3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "6a62fefefde6b2c0d8b3df70151d6a81fc028d28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/6a62fefefde6b2c0d8b3df70151d6a81fc028d28", + "reference": "6a62fefefde6b2c0d8b3df70151d6a81fc028d28", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.3.0,<2.5-dev", + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2013-05-21 05:53:02" + }, + { + "name": "doctrine/inflector", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "8b4b3ccec7aafc596e2fc1e593c9f2e78f939c8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b4b3ccec7aafc596e2fc1e593c9f2e78f939c8c", + "reference": "8b4b3ccec7aafc596e2fc1e593c9f2e78f939c8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2013-04-10 16:14:30" + }, + { + "name": "doctrine/lexer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "bc0e1f0cc285127a38c6c8ea88bc5dba2fd53e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/bc0e1f0cc285127a38c6c8ea88bc5dba2fd53e94", + "reference": "bc0e1f0cc285127a38c6c8ea88bc5dba2fd53e94", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2013-03-07 12:15:25" + }, + { + "name": "evenement/evenement", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "8b0918f8374327dfed4408fe467980ab41d556dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/8b0918f8374327dfed4408fe467980ab41d556dd", + "reference": "8b0918f8374327dfed4408fe467980ab41d556dd", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch", + "homepage": "http://wiedler.ch/igor/" + } + ], + "description": "Événement is a very simple event dispatching library for PHP 5.3", + "keywords": [ + "event-dispatcher" + ], + "time": "2012-12-29 17:04:52" + }, + { + "name": "filp/whoops", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "1.0.6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/1.0.6", + "reference": "1.0.6", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "mockery/mockery": "dev-master", + "silex/silex": "1.0.*@dev" + }, + "type": "library", + "autoload": { + "psr-0": { + "Whoops": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://github.com/filp/whoops", + "keywords": [ + "error", + "exception", + "handling", + "library", + "silex-provider", + "whoops", + "zf2" + ], + "time": "2013-05-10 22:13:22" + }, + { + "name": "guzzle/common", + "version": "dev-master", + "target-dir": "Guzzle/Common", + "source": { + "type": "git", + "url": "https://github.com/guzzle/common.git", + "reference": "v3.6.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/common/zipball/v3.6.0", + "reference": "v3.6.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "symfony/event-dispatcher": ">=2.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle\\Common": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Common libraries used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "collection", + "common", + "event", + "exception" + ], + "time": "2013-05-30 07:01:25" + }, + { + "name": "guzzle/http", + "version": "dev-master", + "target-dir": "Guzzle/Http", + "source": { + "type": "git", + "url": "https://github.com/guzzle/http.git", + "reference": "v3.6.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/http/zipball/v3.6.0", + "reference": "v3.6.0", + "shasum": "" + }, + "require": { + "guzzle/common": "self.version", + "guzzle/parser": "self.version", + "guzzle/stream": "self.version", + "php": ">=5.3.2" + }, + "suggest": { + "ext-curl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle\\Http": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "HTTP libraries used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "client", + "curl", + "http", + "http client" + ], + "time": "2013-05-30 07:01:25" + }, + { + "name": "guzzle/parser", + "version": "dev-master", + "target-dir": "Guzzle/Parser", + "source": { + "type": "git", + "url": "https://github.com/guzzle/parser.git", + "reference": "v3.6.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.6.0", + "reference": "v3.6.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle\\Parser": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Interchangeable parsers used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "URI Template", + "cookie", + "http", + "message", + "url" + ], + "time": "2013-05-30 07:01:25" + }, + { + "name": "guzzle/stream", + "version": "dev-master", + "target-dir": "Guzzle/Stream", + "source": { + "type": "git", + "url": "https://github.com/guzzle/stream.git", + "reference": "v3.6.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.6.0", + "reference": "v3.6.0", + "shasum": "" + }, + "require": { + "guzzle/common": "self.version", + "php": ">=5.3.2" + }, + "suggest": { + "guzzle/http": "To convert Guzzle request objects to PHP streams" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle\\Stream": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle stream wrapper component", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "component", + "stream" + ], + "time": "2013-05-30 07:01:25" + }, + { + "name": "ircmaxell/password-compat", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "v1.0.3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/v1.0.3", + "reference": "v1.0.3", + "shasum": "" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2013-04-30 19:58:08" + }, + { + "name": "kriswallsmith/assetic", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/assetic.git", + "reference": "d5311bf231ecf8a1e4b8ae00dcb15651b63dfed5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/d5311bf231ecf8a1e4b8ae00dcb15651b63dfed5", + "reference": "d5311bf231ecf8a1e4b8ae00dcb15651b63dfed5", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/process": ">=2.1,<3.0" + }, + "require-dev": { + "cssmin/cssmin": "*", + "joliclic/javascript-packer": "*", + "kamicane/packager": "*", + "leafo/lessphp": "*", + "leafo/scssphp": "*", + "leafo/scssphp-compass": "*", + "mrclay/minify": "*", + "phpunit/phpunit": ">=3.7,<4.0", + "ptachoire/cssembed": "*", + "twig/twig": ">=1.6,<2.0" + }, + "suggest": { + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", + "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler", + "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin", + "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris", + "twig/twig": "Assetic provides the integration with the Twig templating engine" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-0": { + "Assetic": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Asset Management for PHP", + "homepage": "https://github.com/kriswallsmith/assetic", + "keywords": [ + "assets", + "compression", + "minification" + ], + "time": "2013-06-04 14:31:31" + }, + { + "name": "laravel/framework", + "version": "4.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "444dbc5d02fa1e10737fcb06dd7124731f88a819" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/444dbc5d02fa1e10737fcb06dd7124731f88a819", + "reference": "444dbc5d02fa1e10737fcb06dd7124731f88a819", + "shasum": "" + }, + "require": { + "classpreloader/classpreloader": "1.0.*", + "doctrine/dbal": "2.3.x", + "filp/whoops": "1.0.6", + "ircmaxell/password-compat": "1.0.*", + "monolog/monolog": "1.5.*", + "nesbot/carbon": "1.*", + "patchwork/utf8": "1.1.*", + "php": ">=5.3.0", + "predis/predis": "0.8.*", + "swiftmailer/swiftmailer": "5.0.*", + "symfony/browser-kit": "2.3.*", + "symfony/console": "2.3.*", + "symfony/css-selector": "2.3.*", + "symfony/debug": "2.3.*", + "symfony/dom-crawler": "2.3.*", + "symfony/event-dispatcher": "2.3.*", + "symfony/finder": "2.3.*", + "symfony/http-foundation": "2.3.*", + "symfony/http-kernel": "2.3.*", + "symfony/process": "2.3.*", + "symfony/routing": "2.3.*", + "symfony/translation": "2.3.*" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/cache": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/exception": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/foundation": "self.version", + "illuminate/hashing": "self.version", + "illuminate/html": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/mail": "self.version", + "illuminate/pagination": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "illuminate/workbench": "self.version" + }, + "require-dev": { + "aws/aws-sdk-php": "2.2.*", + "iron-io/iron_mq": "1.4.4", + "mockery/mockery": "0.7.2", + "pda/pheanstalk": "2.0.*", + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + [ + "src/Illuminate/Queue/IlluminateQueueClosure.php" + ] + ], + "files": [ + "src/Illuminate/Support/helpers.php" + ], + "psr-0": { + "Illuminate": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylorotwell@gmail.com" + } + ], + "description": "The Laravel Framework.", + "keywords": [ + "framework", + "laravel" + ], + "time": "2013-06-04 21:58:42" + }, + { + "name": "monolog/monolog", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "1.5.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1.5.0", + "reference": "1.5.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": ">=1.0,<2.0" + }, + "require-dev": { + "doctrine/couchdb": "dev-master", + "mlehner/gelf-php": "1.0.*", + "raven/raven": "0.3.*" + }, + "suggest": { + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server", + "raven/raven": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Monolog": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be", + "role": "Developer" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2013-04-23 10:09:48" + }, + { + "name": "nesbot/carbon", + "version": "1.2.0", + "source": { + "type": "git", + "url": "git://github.com/briannesbitt/Carbon.git", + "reference": "1.2.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/briannesbitt/Carbon/zipball/1.2.0", + "reference": "1.2.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Carbon": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "https://github.com/briannesbitt/Carbon", + "keywords": [ + "date", + "datetime", + "time" + ], + "time": "2012-10-14 17:41:18" + }, + { + "name": "nikic/php-parser", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "5ccf6196d6925e66568e3b8460c262e9512e4b92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/5ccf6196d6925e66568e3b8460c262e9512e4b92", + "reference": "5ccf6196d6925e66568e3b8460c262e9512e4b92", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9-dev" + } + }, + "autoload": { + "psr-0": { + "PHPParser": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2013-05-23 13:17:59" + }, + { + "name": "patchwork/utf8", + "version": "v1.1.8", + "source": { + "type": "git", + "url": "https://github.com/nicolas-grekas/Patchwork-UTF8.git", + "reference": "v1.1.8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nicolas-grekas/Patchwork-UTF8/zipball/v1.1.8", + "reference": "v1.1.8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Patchwork": "class/", + "Normalizer": "class/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "(Apache-2.0 or GPL-2.0)" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com", + "role": "Developer" + } + ], + "description": "UTF-8 strings handling for PHP 5.3: portable, performant and extended", + "homepage": "https://github.com/nicolas-grekas/Patchwork-UTF8", + "keywords": [ + "i18n", + "unicode", + "utf-8", + "utf8" + ], + "time": "2013-05-24 12:11:22" + }, + { + "name": "predis/predis", + "version": "0.8.x-dev", + "source": { + "type": "git", + "url": "https://github.com/nrk/predis.git", + "reference": "aa458a1922a99611d7f81795bedff88459bc8753" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nrk/predis/zipball/aa458a1922a99611d7f81795bedff88459bc8753", + "reference": "aa458a1922a99611d7f81795bedff88459bc8753", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "autoload": { + "psr-0": { + "Predis": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net" + } + ], + "description": "Flexible and feature-complete PHP client library for Redis", + "homepage": "http://github.com/nrk/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "time": "2013-06-03 10:04:10" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/php-fig/log/archive/1.0.0.zip", + "reference": "1.0.0", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "react/event-loop", + "version": "dev-master", + "target-dir": "React/EventLoop", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "v0.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/v0.3.2", + "reference": "v0.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-libev": "*", + "ext-libevent": ">=0.0.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + }, + "autoload": { + "psr-0": { + "React\\EventLoop": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event loop abstraction layer that libraries can use for evented I/O.", + "keywords": [ + "event-loop" + ], + "time": "2013-01-14 23:11:47" + }, + { + "name": "react/socket", + "version": "dev-master", + "target-dir": "React/Socket", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "v0.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/v0.3.2", + "reference": "v0.3.2", + "shasum": "" + }, + "require": { + "evenement/evenement": "1.0.*", + "php": ">=5.3.3", + "react/event-loop": "0.3.*", + "react/stream": "0.3.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + }, + "autoload": { + "psr-0": { + "React\\Socket": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Library for building an evented socket server.", + "keywords": [ + "Socket" + ], + "time": "2013-04-26 20:23:10" + }, + { + "name": "react/stream", + "version": "dev-master", + "target-dir": "React/Stream", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "v0.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/v0.3.2", + "reference": "v0.3.2", + "shasum": "" + }, + "require": { + "evenement/evenement": "1.0.*", + "php": ">=5.3.3" + }, + "suggest": { + "react/event-loop": "0.3.*", + "react/promise": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + }, + "autoload": { + "psr-0": { + "React\\Stream": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Basic readable and writable stream interfaces that support piping.", + "keywords": [ + "pipe", + "stream" + ], + "time": "2013-05-10 15:12:22" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "v5.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/v5.0.0", + "reference": "v5.0.0", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Chris Corbyn" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "mail", + "mailer" + ], + "time": "2013-04-30 17:35:30" + }, + { + "name": "symfony/browser-kit", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/BrowserKit", + "source": { + "type": "git", + "url": "https://github.com/symfony/BrowserKit.git", + "reference": "v2.3.0-RC1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/v2.3.0-RC1", + "reference": "v2.3.0-RC1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/dom-crawler": ">=2.0,<3.0" + }, + "require-dev": { + "symfony/css-selector": ">=2.0,<3.0", + "symfony/process": ">=2.0,<3.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\BrowserKit\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "http://symfony.com", + "time": "2013-05-15 15:16:47" + }, + { + "name": "symfony/console", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/Console", + "source": { + "type": "git", + "url": "https://github.com/symfony/Console.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Console/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/event-dispatcher": ">=2.1,<3.0" + }, + "suggest": { + "symfony/event-dispatcher": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "http://symfony.com", + "time": "2013-05-30 05:11:26" + }, + { + "name": "symfony/css-selector", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/CssSelector", + "source": { + "type": "git", + "url": "https://github.com/symfony/CssSelector.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/CssSelector/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\CssSelector\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "http://symfony.com", + "time": "2013-05-19 18:59:12" + }, + { + "name": "symfony/debug", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/Debug", + "source": { + "type": "git", + "url": "https://github.com/symfony/Debug.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Debug/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/http-foundation": ">=2.1,<3.0", + "symfony/http-kernel": ">=2.1,<3.0" + }, + "suggest": { + "symfony/class-loader": "", + "symfony/http-foundation": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Debug\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "http://symfony.com", + "time": "2013-06-02 11:58:44" + }, + { + "name": "symfony/dom-crawler", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/DomCrawler", + "source": { + "type": "git", + "url": "https://github.com/symfony/DomCrawler.git", + "reference": "3cf81e7a021853183aa303181afc6e6868bf48ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/3cf81e7a021853183aa303181afc6e6868bf48ce", + "reference": "3cf81e7a021853183aa303181afc6e6868bf48ce", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/css-selector": ">=2.0,<3.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\DomCrawler\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "http://symfony.com", + "time": "2013-05-19 19:00:48" + }, + { + "name": "symfony/event-dispatcher", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "v2.3.0-RC1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.0-RC1", + "reference": "v2.3.0-RC1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": ">=2.0,<3.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com", + "time": "2013-05-13 14:36:40" + }, + { + "name": "symfony/filesystem", + "version": "dev-master", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "3567f5f48305098044c6d6a383f5cefec9c45efa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/3567f5f48305098044c6d6a383f5cefec9c45efa", + "reference": "3567f5f48305098044c6d6a383f5cefec9c45efa", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2013-05-16 07:54:39" + }, + { + "name": "symfony/finder", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2013-06-02 12:05:51" + }, + { + "name": "symfony/http-foundation", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/HttpFoundation", + "source": { + "type": "git", + "url": "https://github.com/symfony/HttpFoundation.git", + "reference": "v2.3.0-RC1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.3.0-RC1", + "reference": "v2.3.0-RC1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "classmap": [ + "Symfony/Component/HttpFoundation/Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "http://symfony.com", + "time": "2013-05-10 06:00:03" + }, + { + "name": "symfony/http-kernel", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/HttpKernel", + "source": { + "type": "git", + "url": "https://github.com/symfony/HttpKernel.git", + "reference": "4f0f6485abe0e2e8b8a94369fb98b8447fb1e3cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/4f0f6485abe0e2e8b8a94369fb98b8447fb1e3cc", + "reference": "4f0f6485abe0e2e8b8a94369fb98b8447fb1e3cc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "psr/log": ">=1.0,<2.0", + "symfony/debug": ">=2.3,<3.0", + "symfony/event-dispatcher": ">=2.1,<3.0", + "symfony/http-foundation": ">=2.2,<3.0" + }, + "require-dev": { + "symfony/browser-kit": "2.2.*", + "symfony/class-loader": ">=2.1,<3.0", + "symfony/config": ">=2.0,<3.0", + "symfony/console": "2.2.*", + "symfony/dependency-injection": ">=2.0,<3.0", + "symfony/finder": ">=2.0,<3.0", + "symfony/process": ">=2.0,<3.0", + "symfony/routing": ">=2.2,<3.0", + "symfony/stopwatch": ">=2.2,<3.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/class-loader": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\HttpKernel\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony HttpKernel Component", + "homepage": "http://symfony.com", + "time": "2013-06-03 15:11:57" + }, + { + "name": "symfony/process", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/Process", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "v2.3.0-RC1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/v2.3.0-RC1", + "reference": "v2.3.0-RC1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "http://symfony.com", + "time": "2013-05-06 20:03:44" + }, + { + "name": "symfony/routing", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/Routing", + "source": { + "type": "git", + "url": "https://github.com/symfony/Routing.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "doctrine/common": ">=2.2,<3.0", + "psr/log": ">=1.0,<2.0", + "symfony/config": ">=2.2,<3.0", + "symfony/yaml": ">=2.0,<3.0" + }, + "suggest": { + "doctrine/common": "", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Routing\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Routing Component", + "homepage": "http://symfony.com", + "time": "2013-05-20 08:57:26" + }, + { + "name": "symfony/translation", + "version": "2.3.x-dev", + "target-dir": "Symfony/Component/Translation", + "source": { + "type": "git", + "url": "https://github.com/symfony/Translation.git", + "reference": "v2.3.0-RC1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Translation/zipball/v2.3.0-RC1", + "reference": "v2.3.0-RC1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/config": ">=2.0,<3.0", + "symfony/yaml": ">=2.2,<3.0" + }, + "suggest": { + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "http://symfony.com", + "time": "2013-05-13 14:36:40" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "kriswallsmith/assetic": 20 + }, + "platform": [ + + ], + "platform-dev": [ + + ] +} diff --git a/composer.phar b/composer.phar new file mode 100644 index 0000000000000000000000000000000000000000..c1b019309793cafee05971e6d0a19de87f8d2ed4 GIT binary patch literal 807659 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

@gq0s+ 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 O(mo~z1_pO0Unc;c6r|k% literal 0 HcmV?d00001 diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 00000000..c42dc4f7 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,18 @@ + + + + + ./app/tests/ + + + \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 00000000..1bc5c957 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,9 @@ + + Options -MultiViews + RewriteEngine On + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + RewriteRule ^(.*\.(?:coffee))$ /asset.php?type=coffee&file=/$1 [L,QSA,NC] + RewriteRule ^(.*\.(?:less))$ /asset.php?type=less&file=/$1 [L,QSA,NC] + \ No newline at end of file diff --git a/public/asset.php b/public/asset.php new file mode 100644 index 00000000..8efe5c23 --- /dev/null +++ b/public/asset.php @@ -0,0 +1,66 @@ +ensureFilter(new UglifyJs2Filter(Config::get('app.uglify-js'))); + $bundle->setTargetPath('scripts'); + } else { + $filePath = trim($_GET['file'], '/'); + $bundle = new AssetCollection([new FileAsset($filePath)], [new CoffeeScriptFilter(Config::get('app.coffee'))]); + $bundle->setTargetPath($filePath); + } + + $bundle = new AssetCache($bundle, new FilesystemCache("$cacheDirectory/scripts")); + } else if ($_GET['type'] == 'less') { + header('Content-Type: text/css'); + + if (!isset($_GET['file']) || !Config::get('app.debug')) { + $bundle = Assets::styleAssetCollection($_GET['area']); + $bundle->ensureFilter(new UglifyCssFilter(Config::get('app.uglify-css'))); + $bundle->setTargetPath('styles'); + } else { + $filePath = trim($_GET['file'], '/'); + $lastModifiedCollection = new AssetCollection([new GlobAsset("styles/*.less")]); + $bundle = new AssetCollection([new FileAsset($filePath), new CacheBusterAsset($lastModifiedCollection->getLastModified())], [new LessFilter('node')]); + $bundle->setTargetPath($filePath); + } + + $bundle = new AssetCache($bundle, new FilesystemCache("$cacheDirectory/styles")); + } else { + exit(); + } + + $time = gmdate($bundle->getLastModified()); + + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $time == $_SERVER['HTTP_IF_MODIFIED_SINCE']) { + header('HTTP/1.0 304 Not Modified'); + exit(); + } + + header('Last-Modified: ' . $time); + header('Cache-Control: max-age=' . (60 * 60 * 24 * 7)); + + echo $bundle->dump(); \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..e69de29b diff --git a/public/fonts/FontAwesome.otf b/public/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..32dd8b1cd5410a0ce93305be80d53a63dbb9b5cd GIT binary patch literal 50204 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 IX~(Sp0L^L&n*aa+ literal 0 HcmV?d00001 diff --git a/public/fonts/fontawesome-webfont.eot b/public/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..c080283bdd786eb40d77cb00eab751e2098f51c5 GIT binary patch literal 29360 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 literal 0 HcmV?d00001 diff --git a/public/fonts/fontawesome-webfont.svg b/public/fonts/fontawesome-webfont.svg new file mode 100644 index 00000000..10a1e1bb --- /dev/null +++ b/public/fonts/fontawesome-webfont.svg @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/fontawesome-webfont.ttf b/public/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..908f69ec9a701858116376eee7b3e4ecb76da25b GIT binary patch literal 64960 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 literal 0 HcmV?d00001 diff --git a/public/fonts/fontawesome-webfont.woff b/public/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..a33af950aedc0044f1082e21cd714244782e2b12 GIT binary patch literal 34420 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 B7G?kd literal 0 HcmV?d00001 diff --git a/public/images/glyphicons-halflings.png b/public/images/glyphicons-halflings.png new file mode 100644 index 0000000000000000000000000000000000000000..a9969993201f9cee63cf9f49217646347297b643 GIT binary patch literal 12799 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 literal 0 HcmV?d00001 diff --git a/public/index.php b/public/index.php new file mode 100644 index 00000000..630a0d06 --- /dev/null +++ b/public/index.php @@ -0,0 +1,62 @@ + + */ + +/* +|-------------------------------------------------------------------------- +| Register The Auto Loader +|-------------------------------------------------------------------------- +| +| Composer provides a convenient, automatically generated class loader +| for our application. We just need to utilize it! We'll require it +| into the script here so that we do not have to worry about the +| loading of any our classes "manually". Feels great to relax. +| +*/ + +require __DIR__.'/../bootstrap/autoload.php'; + +/* +|-------------------------------------------------------------------------- +| Turn On The Lights +|-------------------------------------------------------------------------- +| +| We need to illuminate PHP development, so let's turn on the lights. +| This bootstrap the framework and gets it ready for use, then it +| will load up this application so that we can run it and send +| the responses back to the browser and delight these users. +| +*/ + +$app = require_once __DIR__.'/../bootstrap/start.php'; + +/* +|-------------------------------------------------------------------------- +| Run The Application +|-------------------------------------------------------------------------- +| +| Once we have the application, we can simply call the run method, +| which will execute the request and send the response back to +| the client's browser allowing them to enjoy the creative +| and wonderful applications we have created for them. +| +*/ + +$app->run(); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once the app has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$app->shutdown(); \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000..9e60f970 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/public/scripts/app/app.coffee b/public/scripts/app/app.coffee new file mode 100644 index 00000000..b0d6f0eb --- /dev/null +++ b/public/scripts/app/app.coffee @@ -0,0 +1,120 @@ +angular.module 'ponyfm', ['ui.bootstrap', 'ui.state'], [ + '$routeProvider', '$locationProvider', '$stateProvider', '$dialogProvider' + (route, location, state, $dialogProvider) -> + + # Account + + state.state 'account', + url: '/account' + templateUrl: '/templates/account/settings.html' + controller: 'account-settings' + + state.state 'account-content', + url: '/account/content' + abstract: true + templateUrl: '/templates/account/content/_layout.html' + + state.state 'account-content.tracks', + url: '/tracks' + templateUrl: '/templates/account/content/tracks.html' + controller: 'account-content-tracks' + + state.state 'account-content.tracks.edit', + url: '/:track_id' + + state.state 'account-content.albums', + url: '/albums' + templateUrl: '/templates/account/content/albums.html' + + state.state 'account-content.playlists', + url: '/playlists' + templateUrl: '/templates/account/content/playlists.html' + + state.state 'account-favorites', + url: '/account/favorites' + abstract: true + templateUrl: '/templates/account/favorites/_layout.html' + + state.state 'account-favorites.tracks', + url: '' + templateUrl: '/templates/account/favorites/tracks.html' + + state.state 'account-favorites.playlists', + url: '/playlists' + templateUrl: '/templates/account/favorites/playlists.html' + + state.state 'account-favorites.albums', + url: '/albums' + templateUrl: '/templates/account/favorites/albums.html' + + # Tracks + + state.state 'tracks', + url: '/tracks' + templateUrl: '/templates/tracks/_layout.html' + abstract: true + + state.state 'tracks.list', + url: '' + templateUrl: '/templates/tracks/index.html' + + state.state 'tracks.random', + url: '/random' + templateUrl: '/templates/tracks/index.html' + + state.state 'tracks.popular', + url: '/popular' + templateUrl: '/templates/tracks/index.html' + + # Albums + + state.state 'albums', + url: '/albums' + templateUrl: '/templates/albums/index.html' + + # Playlists + + state.state 'playlists', + url: '/playlists' + templateUrl: '/templates/playlists/index.html' + + # Artists + + state.state 'artists', + url: '/artists' + templateUrl: '/templates/artists/index.html' + + # Pages + + state.state 'faq', + url: '/faq' + templateUrl: '/templates/pages/faq.html' + + state.state 'about', + url: '/about' + templateUrl: '/templates/pages/about.html' + + # Auth + + state.state 'login', + url: '/login' + templateUrl: '/templates/auth/login.html' + controller: 'login' + + state.state 'register', + url: '/register' + templateUrl: '/templates/auth/register.html' + + # Hompage + + state.state 'home', + url: '/' + templateUrl: '/templates/home/index.html' + + route.otherwise '/' + + location.html5Mode(true); + $dialogProvider.options + dialogFade: true + backdropClick: false +] \ No newline at end of file diff --git a/public/scripts/app/controllers/account-content-tracks.coffee b/public/scripts/app/controllers/account-content-tracks.coffee new file mode 100644 index 00000000..828ab080 --- /dev/null +++ b/public/scripts/app/controllers/account-content-tracks.coffee @@ -0,0 +1,177 @@ +angular.module('ponyfm').controller "account-content-tracks", [ + '$scope', '$state', 'taxonomies', '$dialog' + ($scope, $state, taxonomies, $dialog) -> + $scope.selectedTrack = null + $scope.isDirty = false + $scope.taxonomies = + trackTypes: taxonomies.trackTypes + licenses: taxonomies.licenses + genres: taxonomies.genres + + $scope.updateIsVocal = () -> + delete $scope.errors.lyrics if !$scope.edit.is_vocal + + $scope.updateTrack = (track) -> + xhr = new XMLHttpRequest() + xhr.onload = -> $scope.$apply -> + if xhr.status != 200 + errors = + if xhr.getResponseHeader('content-type') == 'application/json' + $.parseJSON(xhr.responseText).errors + else + ['There was an unknown error!'] + + $scope.errors = {} + _.each errors, (value, key) -> $scope.errors[key] = value.join ', ' + return + + $scope.selectedTrack.is_published = true + selectTrack $scope.selectedTrack + + formData = new FormData(); + _.each $scope.edit, (value, name) -> + if name == 'cover' + formData.append name, value, value.name + else + formData.append name, value + + xhr.open 'POST', '/api/web/tracks/edit/' + $scope.edit.id, true + xhr.setRequestHeader 'X-Token', pfm.token + xhr.send formData + + $scope.uploadTrackCover = () -> + $("#coverImage").trigger 'click' + + $scope.setCoverImage = (input) -> + $scope.$apply -> + previewElement = $('#coverPreview')[0] + file = input.files[0] + + if file.type != 'image/png' + $scope.errors.cover = 'Cover image must be a png!' + return + + delete $scope.errors.cover + $scope.isDirty = true + reader = new FileReader() + reader.onload = (e) -> previewElement.src = e.target.result + reader.readAsDataURL file + $scope.edit.cover = file + + $scope.filters = + published: [ + {title: 'Either', query: ''}, + {title: 'Yes', query: 'published=1'}, + {title: 'No', query: 'published=0'}] + + sort: [ + {title: 'Newest to Oldest', query: 'order=created_at,desc'}, + {title: 'Oldest to Newest', query: 'order=created_at,asc'}] + + genres: {} + trackTypes: {} + + $scope.filter = + published: $scope.filters.published[0] + sort: $scope.filters.sort[0] + genres: {} + trackTypes: {} + + $scope.titles = + genres: 'All' + trackTypes: 'All' + + taxonomies.refresh().done () -> + for genre in taxonomies.genres + $scope.filters.genres[genre.id] = + id: genre.id + title: genre.name + query: 'genres[]=' + genre.id + for type in taxonomies.trackTypes + $scope.filters.trackTypes[type.id] = + id: type.id + title: type.title + query: 'types[]=' + type.id + + $scope.updateFilter = (type, filter) -> + $scope.filter[type] = filter + $scope.refreshList() + + $scope.toggleFilter = (type, id) -> + if !$scope.filter[type][id] + $scope.filter[type][id] = $scope.filters[type][id] + else + delete $scope.filter[type][id] + + length = _.keys($scope.filter[type]).length + if length == 1 + $scope.titles[type] = _.map($scope.filter[type], (f) -> f.title).join ', ' + else if length > 1 + $scope.titles[type] = length + ' selected' + else + $scope.titles[type] = 'All' + + $scope.refreshList() + + $scope.refreshList = () -> + parts = [$scope.filter.sort.query, $scope.filter.published.query] + _.each $scope.filter.genres, (g) -> parts.push g.query + _.each $scope.filter.trackTypes, (g) -> parts.push g.query + query = parts.join '&' + $.getJSON('/api/web/tracks/owned?' + query).done (tracks) -> $scope.$apply -> showTracks tracks + + tracksDb = {} + + showTracks = (tracks) -> + tracksDb = {} + $scope.tracks = tracks + tracksDb[track.id] = track for track in tracks + + selectTrack = (t) -> + $scope.selectedTrack = t + return if !t + $.getJSON('/api/web/tracks/edit/' + t.id) + .done (track) -> $scope.$apply -> + $scope.isDirty = false + $scope.errors = {} + $scope.edit = + id: track.id + title: track.title + description: track.description + lyrics: track.lyrics + is_explicit: track.is_explicit + is_downloadable: track.is_downloadable + is_vocal: track.is_vocal + license_id: track.license_id + genre_id: track.genre_id + track_type_id: track.track_type_id + released_at: if track.released_at then track.released_at.date else '' + + $scope.touchModel = -> $scope.isDirty = true + + $.getJSON('/api/web/tracks/owned?order=created_at,desc').done (tracks) -> $scope.$apply -> + showTracks tracks + if $state.params.track_id + selectTrack tracksDb[$state.params.track_id] + + $scope.selectTrack = (track) -> $scope.selectedTrack = track + $scope.deleteTrack = (track) -> + $dialog.messageBox('Delete ' + track.title, 'Are you sure you want to delete "' + track.title + '"? This cannot be undone.', [ + {result: 'ok', label: 'Yes', cssClass: 'btn-danger'}, {result: 'cancel', label: 'No', cssClass: 'btn-primary'} + ]).open().then (res) -> + return if res == 'cancel' + selectTrack null if track == $scope.selectedTrack + $.post('/api/web/tracks/delete/' + track.id, {_token: window.pfm.token}) + .then -> + $scope.refreshList() + + $scope.$on '$stateChangeSuccess', () -> + if $state.params.track_id + selectTrack tracksDb[$state.params.track_id] + else + selectTrack null + + $scope.$on '$stateChangeStart', (e) -> + return if $scope.selectedTrack == null || !$scope.isDirty + e.preventDefault() if !confirm('Are you sure you want to leave this page without saving your changes?') +] \ No newline at end of file diff --git a/public/scripts/app/controllers/account-settings.coffee b/public/scripts/app/controllers/account-settings.coffee new file mode 100644 index 00000000..885064f8 --- /dev/null +++ b/public/scripts/app/controllers/account-settings.coffee @@ -0,0 +1,4 @@ +angular.module('ponyfm').controller "account-settings", [ + '$scope', 'auth' + ($scope, auth) -> +] \ No newline at end of file diff --git a/public/scripts/app/controllers/application.coffee b/public/scripts/app/controllers/application.coffee new file mode 100644 index 00000000..1277e6ad --- /dev/null +++ b/public/scripts/app/controllers/application.coffee @@ -0,0 +1,16 @@ +angular.module('ponyfm').controller "application", [ + '$scope', 'auth', '$location', 'upload', '$state', '$stateParams', 'taxonomies' + ($scope, auth, $location, upload, $state, $stateParams, taxonomies) -> + $scope.auth = auth.data + $scope.$state = $state + $scope.$stateParams = $stateParams + + $scope.logout = () -> + auth.logout().done -> location.reload() + + $scope.isActive = (loc) -> $location.path() == loc + $scope.$on '$viewContentLoaded', () -> window.handleResize() + + # Show loading screen here? + taxonomies.refresh() +] \ No newline at end of file diff --git a/public/scripts/app/controllers/login.coffee b/public/scripts/app/controllers/login.coffee new file mode 100644 index 00000000..780aee1d --- /dev/null +++ b/public/scripts/app/controllers/login.coffee @@ -0,0 +1,18 @@ +angular.module('ponyfm').controller "login", [ + '$scope', 'auth' + ($scope, auth) -> + + $scope.messages = [] + + $scope.login = + remember: true + + submit: () -> + $scope.messages = [] + + auth.login(this.email, this.password, this.remember) + .done -> + location.reload() + .fail (messages) -> + $scope.messages = _.values messages +] \ No newline at end of file diff --git a/public/scripts/app/controllers/upload.coffee b/public/scripts/app/controllers/upload.coffee new file mode 100644 index 00000000..43b2d911 --- /dev/null +++ b/public/scripts/app/controllers/upload.coffee @@ -0,0 +1,32 @@ +angular.module('ponyfm').controller "upload", [ + '$scope', 'auth', 'upload', '$state' + ($scope, auth, upload, $state) -> + $scope.$on 'upload-queue-started', () -> + $scope.state = 'uploading' + $scope.uploadDialogOpen = true + $scope.uploads = {} + $scope.progress = 0 + $scope.uploadedFiles = 0 + $scope.totalFiles = 0 + + $scope.$on 'upload-added', (e, upload) -> + $scope.uploads[upload.index] = upload + $scope.totalFiles++ + + $scope.$on 'upload-queue-ended', () -> + $scope.state = 'finished' + $scope.uploadDialogOpen = false if _.each upload.queue, (u) -> u.error == null + $state.transitionTo 'account-content.tracks' + $scope.uploadDialogOpen = false if !(_.size $scope.uploads) + + $scope.$on 'upload-finished', (e, upload) -> + $scope.uploadedFiles++ + delete $scope.uploads[upload.index] if upload.success + + $scope.$on 'upload-progress', () -> + $scope.progress = upload.totalBytesUploaded / upload.totalBytes * 100 + $scope.state = 'processing' if $scope.progress >= 100 + + $scope.close = () -> + $scope.uploadDialogOpen = false +] \ No newline at end of file diff --git a/public/scripts/app/directives/eat-click.coffee b/public/scripts/app/directives/eat-click.coffee new file mode 100644 index 00000000..dc5904b7 --- /dev/null +++ b/public/scripts/app/directives/eat-click.coffee @@ -0,0 +1,4 @@ +angular.module('ponyfm').directive 'pfmEatClick', () -> + (scope, element) -> + $(element).click (e) -> + e.preventDefault() diff --git a/public/scripts/app/directives/progress-bar.coffee b/public/scripts/app/directives/progress-bar.coffee new file mode 100644 index 00000000..31c26185 --- /dev/null +++ b/public/scripts/app/directives/progress-bar.coffee @@ -0,0 +1,5 @@ +angular.module('ponyfm').directive 'pfmProgressBar', () -> + (scope, element, attrs) -> + scope.$watch attrs.pfmProgressBar, (val) -> + return if !val? + $(element).css 'width', Math.floor(val) + '%' \ No newline at end of file diff --git a/public/scripts/app/directives/uploader.coffee b/public/scripts/app/directives/uploader.coffee new file mode 100644 index 00000000..267cf00b --- /dev/null +++ b/public/scripts/app/directives/uploader.coffee @@ -0,0 +1,22 @@ +angular.module('ponyfm').directive 'uploader', [ + 'upload' + (upload) -> (scope) -> + $body = $ 'body' + $notice = $("

").appendTo($body) + notice = $notice[0] + + window.addEventListener 'dragover', (e) -> + e.preventDefault() + $body.addClass 'file-over' + + notice.addEventListener 'dragleave', (e) -> + e.preventDefault() + $body.removeClass 'file-over' + + notice.addEventListener 'drop', (e) -> + e.preventDefault() + $body.removeClass 'file-over' + + files = e.target.files || e.dataTransfer.files + scope.$apply -> upload.upload files +] \ No newline at end of file diff --git a/public/scripts/app/filters/length.coffee b/public/scripts/app/filters/length.coffee new file mode 100644 index 00000000..34ec4680 --- /dev/null +++ b/public/scripts/app/filters/length.coffee @@ -0,0 +1,3 @@ +angular.module('ponyfm').filter 'pfmLength', () -> + (input) -> + input.length \ No newline at end of file diff --git a/public/scripts/app/filters/pfm-date.js b/public/scripts/app/filters/pfm-date.js new file mode 100644 index 00000000..38a5b521 --- /dev/null +++ b/public/scripts/app/filters/pfm-date.js @@ -0,0 +1,216 @@ +/* + If you're wondering, this is indeed a copy/paste of angular's date filter with all of its internal dependencies. + Why, you ask? Well, I needed to add lines 190 and 191, and didn't want to edit the source of angular itself. + Now this filter supports dates returned directly from Carbon. + */ + +angular.module('ponyfm').filter('pfmdate', [ + '$locale', + function($locale) { + function isString(value){return typeof value == 'string';} + function isNumber(value){return typeof value == 'number';} + + function isDate(value){ + return toString.apply(value) == '[object Date]'; + } + + function padNumber(num, digits, trim) { + var neg = ''; + if (num < 0) { + neg = '-'; + num = -num; + } + num = '' + num; + while(num.length < digits) num = '0' + num; + if (trim) + num = num.substr(num.length - digits); + return neg + num; + } + + function int(str) { + return parseInt(str, 10); + } + + function concat(array1, array2, index) { + return array1.concat([].slice.call(array2, index)); + } + + function isArrayLike(obj) { + if (!obj || (typeof obj.length !== 'number')) return false; + + // We have on object which has length property. Should we treat it as array? + if (typeof obj.hasOwnProperty != 'function' && + typeof obj.constructor != 'function') { + // This is here for IE8: it is a bogus object treat it as array; + return true; + } else { + return obj instanceof JQLite || // JQLite + (jQuery && obj instanceof jQuery) || // jQuery + toString.call(obj) !== '[object Object]' || // some browser native object + typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj) + } + } + + function isFunction(value){return typeof value == 'function';} + + function forEach(obj, iterator, context) { + var key; + if (obj) { + if (isFunction(obj)){ + for (key in obj) { + if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } + } + } else if (obj.forEach && obj.forEach !== forEach) { + obj.forEach(iterator, context); + } else if (isArrayLike(obj)) { + for (key = 0; key < obj.length; key++) + iterator.call(context, obj[key], key); + } else { + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } + } + } + } + return obj; + } + + var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, + NUMBER_STRING = /^\d+$/; + + var DATE_FORMATS = { + yyyy: dateGetter('FullYear', 4), + yy: dateGetter('FullYear', 2, 0, true), + y: dateGetter('FullYear', 1), + MMMM: dateStrGetter('Month'), + MMM: dateStrGetter('Month', true), + MM: dateGetter('Month', 2, 1), + M: dateGetter('Month', 1, 1), + dd: dateGetter('Date', 2), + d: dateGetter('Date', 1), + HH: dateGetter('Hours', 2), + H: dateGetter('Hours', 1), + hh: dateGetter('Hours', 2, -12), + h: dateGetter('Hours', 1, -12), + mm: dateGetter('Minutes', 2), + m: dateGetter('Minutes', 1), + ss: dateGetter('Seconds', 2), + s: dateGetter('Seconds', 1), + // while ISO 8601 requires fractions to be prefixed with `.` or `,` + // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions + sss: dateGetter('Milliseconds', 3), + EEEE: dateStrGetter('Day'), + EEE: dateStrGetter('Day', true), + a: ampmGetter, + Z: timeZoneGetter + }; + + function dateGetter(name, size, offset, trim) { + offset = offset || 0; + return function(date) { + var value = date['get' + name](); + if (offset > 0 || value > -offset) + value += offset; + if (value === 0 && offset == -12 ) value = 12; + return padNumber(value, size, trim); + }; + } + + function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date['get' + name](); + var get = uppercase(shortForm ? ('SHORT' + name) : name); + + return formats[get][value]; + }; + } + + function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset(); + var paddedZone = (zone >= 0) ? "+" : ""; + + paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + + padNumber(Math.abs(zone % 60), 2); + + return paddedZone; + } + + function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; + } + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + function jsonStringToDate(string) { + var match; + if (match = string.match(R_ISO8601_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0, + dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, + timeSetter = match[8] ? date.setUTCHours : date.setHours; + + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); + var h = int(match[4]||0) - tzHour; + var m = int(match[5]||0) - tzMin; + var s = int(match[6]||0); + var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + return string; + } + + + return function(date, format) { + var text = '', + parts = [], + fn, match; + + format = format || 'mediumDate'; + format = $locale.DATETIME_FORMATS[format] || format; + if (isString(date)) { + if (NUMBER_STRING.test(date)) { + date = int(date); + } else { + date = jsonStringToDate(date); + } + } + + if (isNumber(date)) { + date = new Date(date); + } + + if (typeof(date) == 'object' && date.date) + date = new Date(date.date); + + if (!isDate(date)) { + return date; + } + + while(format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + forEach(parts, function(value){ + fn = DATE_FORMATS[value]; + text += fn ? fn(date, $locale.DATETIME_FORMATS) + : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); + }); + + return text; + }; + }]); \ No newline at end of file diff --git a/public/scripts/app/services/auth.coffee b/public/scripts/app/services/auth.coffee new file mode 100644 index 00000000..05083a9e --- /dev/null +++ b/public/scripts/app/services/auth.coffee @@ -0,0 +1,18 @@ +angular.module('ponyfm').factory('auth', [ + '$rootScope' + ($rootScope) -> + data: {isLogged: window.pfm.auth.isLogged, user: window.pfm.auth.user} + login: (email, password, remember) -> + def = new $.Deferred() + $.post('/api/web/auth/login', {email: email, password: password, remember: remember, _token: pfm.token}) + .done -> + $rootScope.$apply -> def.resolve() + + .fail (res) -> + $rootScope.$apply -> def.reject res.responseJSON.messages + + def.promise() + + logout: -> $.post('/api/web/auth/logout', {_token: pfm.token}) +]) + diff --git a/public/scripts/app/services/taxonomies.coffee b/public/scripts/app/services/taxonomies.coffee new file mode 100644 index 00000000..990dd912 --- /dev/null +++ b/public/scripts/app/services/taxonomies.coffee @@ -0,0 +1,23 @@ +angular.module('ponyfm').factory('taxonomies', [ + '$rootScope' + ($rootScope) -> + def = null + + self = + trackTypes: [], + licenses: [] + genres: [] + refresh: () -> + return def if def != null + + def = new $.Deferred() + $.getJSON('/api/web/taxonomies/all') + .done (taxonomies) -> $rootScope.$apply -> + self.trackTypes.push t for t in taxonomies.track_types + self.licenses.push t for t in taxonomies.licenses + self.genres.push t for t in taxonomies.genres + def.resolve self + def + + self +]) \ No newline at end of file diff --git a/public/scripts/app/services/upload.coffee b/public/scripts/app/services/upload.coffee new file mode 100644 index 00000000..3eac12e1 --- /dev/null +++ b/public/scripts/app/services/upload.coffee @@ -0,0 +1,63 @@ +angular.module('ponyfm').factory('upload', [ + '$rootScope' + ($rootScope) -> + self = + queue: [] + totalBytes: 0 + totalBytesUploaded: 0 + + upload: (files) -> + $rootScope.$broadcast 'upload-queue-started' if self.queue.length == 0 + + _.each files, (file) -> + upload = + name: file.name + progress: 0 + uploadedSize: 0 + size: file.size + index: self.queue.length + isUploading: true + success: false + error: null + + self.queue.push upload + self.totalBytes += file.size + $rootScope.$broadcast 'upload-added', upload + + xhr = new XMLHttpRequest() + xhr.upload.onprogress = (e) -> + $rootScope.$apply -> + upload.uploadedSize = e.loaded + upload.progress = e.loaded / upload.size * 100 + self.totalBytesUploaded = _.reduce self.queue, ((i, u) -> i + u.uploadedSize), 0 + $rootScope.$broadcast 'upload-progress', upload + + xhr.onload = -> $rootScope.$apply -> + upload.isUploading = false + if xhr.status != 200 + error = + if xhr.getResponseHeader('content-type') == 'application/json' + $.parseJSON(xhr.responseText).message + else + 'There was an unknown error!' + + upload.error = error + $rootScope.$broadcast 'upload-error', [upload, error] + else + upload.success = true + + $rootScope.$broadcast 'upload-finished', upload + + if (_.every self.queue, (u) -> !u.isUploading) + self.queue = [] + self.totalBytes = 0 + self.totalBytesUploaded = 0 + $rootScope.$broadcast 'upload-queue-ended' + + formData = new FormData(); + formData.append('track', file); + + xhr.open 'POST', '/api/web/tracks/upload', true + xhr.setRequestHeader 'X-Token', pfm.token + xhr.send formData +]) \ No newline at end of file diff --git a/public/scripts/base/angular-ui-date.js b/public/scripts/base/angular-ui-date.js new file mode 100644 index 00000000..f405d5a7 --- /dev/null +++ b/public/scripts/base/angular-ui-date.js @@ -0,0 +1,121 @@ +/*global angular */ +/* + jQuery UI Datepicker plugin wrapper + + @note If ≤ IE8 make sure you have a polyfill for Date.toISOString() + @param [ui-date] {object} Options to pass to $.fn.datepicker() merged onto uiDateConfig + */ + +angular.module('ui.date', []) + +.constant('uiDateConfig', {}) + +.directive('uiDate', ['uiDateConfig', '$timeout', function (uiDateConfig, $timeout) { + 'use strict'; + var options; + options = {}; + angular.extend(options, uiDateConfig); + return { + require:'?ngModel', + link:function (scope, element, attrs, controller) { + var getOptions = function () { + return angular.extend({}, uiDateConfig, scope.$eval(attrs.uiDate)); + }; + var initDateWidget = function () { + var showing = false; + var opts = getOptions(); + + // If we have a controller (i.e. ngModelController) then wire it up + if (controller) { + + // Set the view value in a $apply block when users selects + // (calling directive user's function too if provided) + var _onSelect = opts.onSelect || angular.noop; + opts.onSelect = function (value, picker) { + scope.$apply(function() { + showing = true; + controller.$setViewValue(element.datepicker("getDate")); + _onSelect(value, picker); + element.blur(); + }); + }; + opts.beforeShow = function() { + showing = true; + }; + opts.onClose = function(value, picker) { + showing = false; + }; + element.on('blur', function() { + if ( !showing ) { + scope.$apply(function() { + element.datepicker("setDate", element.datepicker("getDate")); + controller.$setViewValue(element.datepicker("getDate")); + }); + } + }); + + // Update the date picker when the model changes + controller.$render = function () { + var date = controller.$viewValue; + if ( angular.isDefined(date) && date !== null && !angular.isDate(date) ) { + throw new Error('ng-Model value must be a Date object - currently it is a ' + typeof date + ' - use ui-date-format to convert it from a string'); + } + element.datepicker("setDate", date); + }; + } + // If we don't destroy the old one it doesn't update properly when the config changes + element.datepicker('destroy'); + // Create the new datepicker widget + element.datepicker(opts); + if ( controller ) { + // Force a render to override whatever is in the input text box + controller.$render(); + } + }; + // Watch for changes to the directives options + scope.$watch(getOptions, initDateWidget, true); + } + }; +} +]) + +.constant('uiDateFormatConfig', '') + +.directive('uiDateFormat', ['uiDateFormatConfig', function(uiDateFormatConfig) { + var directive = { + require:'ngModel', + link: function(scope, element, attrs, modelCtrl) { + var dateFormat = attrs.uiDateFormat || uiDateFormatConfig; + if ( dateFormat ) { + // Use the datepicker with the attribute value as the dateFormat string to convert to and from a string + modelCtrl.$formatters.push(function(value) { + if (angular.isString(value) ) { + return jQuery.datepicker.parseDate(dateFormat, value); + } + return null; + }); + modelCtrl.$parsers.push(function(value){ + if (value) { + return jQuery.datepicker.formatDate(dateFormat, value); + } + return null; + }); + } else { + // Default to ISO formatting + modelCtrl.$formatters.push(function(value) { + if (angular.isString(value) ) { + return new Date(value); + } + return null; + }); + modelCtrl.$parsers.push(function(value){ + if (value) { + return value.toISOString(); + } + return null; + }); + } + } + }; + return directive; +}]); diff --git a/public/scripts/base/angular-ui-router.js b/public/scripts/base/angular-ui-router.js new file mode 100644 index 00000000..285410c2 --- /dev/null +++ b/public/scripts/base/angular-ui-router.js @@ -0,0 +1,1037 @@ +/** + * State-based routing for AngularJS + * @version v0.0.1 + * @link http://angular-ui.github.com/ + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +(function (window, angular, undefined) { +/*jshint globalstrict:true*/ +/*global angular:false*/ +'use strict'; + +var isDefined = angular.isDefined, + isFunction = angular.isFunction, + isString = angular.isString, + isObject = angular.isObject, + isArray = angular.isArray, + forEach = angular.forEach, + extend = angular.extend, + copy = angular.copy; + +function inherit(parent, extra) { + return extend(new (extend(function() {}, { prototype: parent }))(), extra); +} + +/** + * Extends the destination object `dst` by copying all of the properties from the `src` object(s) + * to `dst` if the `dst` object has no own property of the same name. You can specify multiple + * `src` objects. + * + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @see angular.extend + */ +function merge(dst) { + forEach(arguments, function(obj) { + if (obj !== dst) { + forEach(obj, function(value, key) { + if (!dst.hasOwnProperty(key)) dst[key] = value; + }); + } + }); + return dst; +} + +angular.module('ui.util', ['ng']); +angular.module('ui.router', ['ui.util']); +angular.module('ui.state', ['ui.router', 'ui.util']); +angular.module('ui.compat', ['ui.state']); + +/** + * Service. Manages loading of templates. + * @constructor + * @name $templateFactory + * @requires $http + * @requires $templateCache + * @requires $injector + */ +$TemplateFactory.$inject = ['$http', '$templateCache', '$injector']; +function $TemplateFactory( $http, $templateCache, $injector) { + + /** + * Creates a template from a configuration object. + * @function + * @name $templateFactory#fromConfig + * @methodOf $templateFactory + * @param {Object} config Configuration object for which to load a template. The following + * properties are search in the specified order, and the first one that is defined is + * used to create the template: + * @param {string|Function} config.template html string template or function to load via + * {@link $templateFactory#fromString fromString}. + * @param {string|Function} config.templateUrl url to load or a function returning the url + * to load via {@link $templateFactory#fromUrl fromUrl}. + * @param {Function} config.templateProvider function to invoke via + * {@link $templateFactory#fromProvider fromProvider}. + * @param {Object} params Parameters to pass to the template function. + * @param {Object} [locals] Locals to pass to `invoke` if the template is loaded via a + * `templateProvider`. Defaults to `{ params: params }`. + * @return {string|Promise.} The template html as a string, or a promise for that string, + * or `null` if no template is configured. + */ + this.fromConfig = function (config, params, locals) { + return ( + isDefined(config.template) ? this.fromString(config.template, params) : + isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) : + isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) : + null + ); + }; + + /** + * Creates a template from a string or a function returning a string. + * @function + * @name $templateFactory#fromString + * @methodOf $templateFactory + * @param {string|Function} template html template as a string or function that returns an html + * template as a string. + * @param {Object} params Parameters to pass to the template function. + * @return {string|Promise.} The template html as a string, or a promise for that string. + */ + this.fromString = function (template, params) { + return isFunction(template) ? template(params) : template; + }; + + /** + * Loads a template from the a URL via `$http` and `$templateCache`. + * @function + * @name $templateFactory#fromUrl + * @methodOf $templateFactory + * @param {string|Function} url url of the template to load, or a function that returns a url. + * @param {Object} params Parameters to pass to the url function. + * @return {string|Promise.} The template html as a string, or a promise for that string. + */ + this.fromUrl = function (url, params) { + if (isFunction(url)) url = url(params); + if (url == null) return null; + else return $http + .get(url, { cache: $templateCache }) + .then(function(response) { return response.data; }); + }; + + /** + * Creates a template by invoking an injectable provider function. + * @function + * @name $templateFactory#fromUrl + * @methodOf $templateFactory + * @param {Function} provider Function to invoke via `$injector.invoke` + * @param {Object} params Parameters for the template. + * @param {Object} [locals] Locals to pass to `invoke`. Defaults to `{ params: params }`. + * @return {string|Promise.} The template html as a string, or a promise for that string. + */ + this.fromProvider = function (provider, params, locals) { + return $injector.invoke(provider, null, locals || { params: params }); + }; +} + +angular.module('ui.util').service('$templateFactory', $TemplateFactory); + +/** + * Matches URLs against patterns and extracts named parameters from the path or the search + * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list + * of search parameters. Multiple search parameter names are separated by '&'. Search parameters + * do not influence whether or not a URL is matched, but their values are passed through into + * the matched parameters returned by {@link UrlMatcher#exec exec}. + * + * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace + * syntax, which optionally allows a regular expression for the parameter to be specified: + * + * * ':' name - colon placeholder + * * '*' name - catch-all placeholder + * * '{' name '}' - curly placeholder + * * '{' name ':' regexp '}' - curly placeholder with regexp. Should the regexp itself contain + * curly braces, they must be in matched pairs or escaped with a backslash. + * + * Parameter names may contain only word characters (latin letters, digits, and underscore) and + * must be unique within the pattern (across both path and search parameters). For colon + * placeholders or curly placeholders without an explicit regexp, a path parameter matches any + * number of characters other than '/'. For catch-all placeholders the path parameter matches + * any number of characters. + * + * ### Examples + * + * * '/hello/' - Matches only if the path is exactly '/hello/'. There is no special treatment for + * trailing slashes, and patterns have to match the entire path, not just a prefix. + * * '/user/:id' - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or + * '/user/bob/details'. The second path segment will be captured as the parameter 'id'. + * * '/user/{id}' - Same as the previous example, but using curly brace syntax. + * * '/user/{id:[^/]*}' - Same as the previous example. + * * '/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id + * parameter consists of 1 to 8 hex digits. + * * '/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the + * path into the parameter 'path'. + * * '/files/*path' - ditto. + * + * @constructor + * @param {string} pattern the pattern to compile into a matcher. + * + * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any + * URL matching this matcher (i.e. any string for which {@link UrlMatcher#exec exec()} returns + * non-null) will start with this prefix. + */ +function UrlMatcher(pattern) { + + // Find all placeholders and create a compiled pattern, using either classic or curly syntax: + // '*' name + // ':' name + // '{' name '}' + // '{' name ':' regexp '}' + // The regular expression is somewhat complicated due to the need to allow curly braces + // inside the regular expression. The placeholder regexp breaks down as follows: + // ([:*])(\w+) classic placeholder ($1 / $2) + // \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp ... ($4) + // (?: ... | ... | ... )+ the regexp consists of any number of atoms, an atom being either + // [^{}\\]+ - anything other than curly braces or backslash + // \\. - a backslash escape + // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms + var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + names = {}, compiled = '^', last = 0, m, + segments = this.segments = [], + params = this.params = []; + + function addParameter(id) { + if (!/^\w+$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); + if (names[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'"); + names[id] = true; + params.push(id); + } + + function quoteRegExp(string) { + return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&"); + } + + this.source = pattern; + + // Split into static segments separated by path parameter placeholders. + // The number of segments is always 1 more than the number of parameters. + var id, regexp, segment; + while ((m = placeholder.exec(pattern))) { + id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null + regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*'); + segment = pattern.substring(last, m.index); + if (segment.indexOf('?') >= 0) break; // we're into the search part + compiled += quoteRegExp(segment) + '(' + regexp + ')'; + addParameter(id); + segments.push(segment); + last = placeholder.lastIndex; + } + segment = pattern.substring(last); + + // Find any search parameter names and remove them from the last segment + var i = segment.indexOf('?'); + if (i >= 0) { + var search = this.sourceSearch = segment.substring(i); + segment = segment.substring(0, i); + this.sourcePath = pattern.substring(0, last+i); + + // Allow parameters to be separated by '?' as well as '&' to make concat() easier + forEach(search.substring(1).split(/[&?]/), addParameter); + } else { + this.sourcePath = pattern; + this.sourceSearch = ''; + } + + compiled += quoteRegExp(segment) + '$'; + segments.push(segment); + this.regexp = new RegExp(compiled); + this.prefix = segments[0]; +} + +/** + * Returns a new matcher for a pattern constructed by appending the path part and adding the + * search parameters of the specified pattern to this pattern. The current pattern is not + * modified. This can be understood as creating a pattern for URLs that are relative to (or + * suffixes of) the current pattern. + * + * ### Example + * The following two matchers are equivalent: + * ``` + * new UrlMatcher('/user/{id}?q').concat('/details?date'); + * new UrlMatcher('/user/{id}/details?q&date'); + * ``` + * + * @param {string} pattern The pattern to append. + * @return {UrlMatcher} A matcher for the concatenated pattern. + */ +UrlMatcher.prototype.concat = function (pattern) { + // Because order of search parameters is irrelevant, we can add our own search + // parameters to the end of the new pattern. Parse the new pattern by itself + // and then join the bits together, but it's much easier to do this on a string level. + return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch); +}; + +UrlMatcher.prototype.toString = function () { + return this.source; +}; + +/** + * Tests the specified path against this matcher, and returns an object containing the captured + * parameter values, or null if the path does not match. The returned object contains the values + * of any search parameters that are mentioned in the pattern, but their value may be null if + * they are not present in `searchParams`. This means that search parameters are always treated + * as optional. + * + * ### Example + * ``` + * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { x:'1', q:'hello' }); + * // returns { id:'bob', q:'hello', r:null } + * ``` + * + * @param {string} path The URL path to match, e.g. `$location.path()`. + * @param {Object} searchParams URL search parameters, e.g. `$location.search()`. + * @return {Object} The captured parameter values. + */ +UrlMatcher.prototype.exec = function (path, searchParams) { + var m = this.regexp.exec(path); + if (!m) return null; + + var params = this.params, nTotal = params.length, + nPath = this.segments.length-1, + values = {}, i; + + for (i=0; i} An array of parameter names. Must be treated as read-only. If the + * pattern has no parameters, an empty array is returned. + */ +UrlMatcher.prototype.parameters = function () { + return this.params; +}; + +/** + * Creates a URL that matches this pattern by substituting the specified values + * for the path and search parameters. Null values for path parameters are + * treated as empty strings. + * + * ### Example + * ``` + * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' }); + * // returns '/user/bob?q=yes' + * ``` + * + * @param {Object} values the values to substitute for the parameters in this pattern. + * @return {string} the formatted URL (path and optionally search part). + */ +UrlMatcher.prototype.format = function (values) { + var segments = this.segments, params = this.params; + if (!values) return segments.join(''); + + var nPath = segments.length-1, nTotal = params.length, + result = segments[0], i, search, value; + + for (i=0; i= 0) throw new Error("State must have a valid name"); + if (states[name]) throw new Error("State '" + name + "'' is already defined"); + + // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined. + var parent = root; + if (!isDefined(state.parent)) { + // regex matches any valid composite state name + // would match "contact.list" but not "contacts" + var compositeName = /^(.+)\.[^.]+$/.exec(name); + if (compositeName != null) { + parent = findState(compositeName[1]); + } + } else if (state.parent != null) { + parent = findState(state.parent); + } + state.parent = parent; + // state.children = []; + // if (parent) parent.children.push(state); + + // Build a URLMatcher if necessary, either via a relative or absolute URL + var url = state.url; + if (isString(url)) { + if (url.charAt(0) == '^') { + url = state.url = $urlMatcherFactory.compile(url.substring(1)); + } else { + url = state.url = (parent.navigable || root).url.concat(url); + } + } else if (isObject(url) && + isFunction(url.exec) && isFunction(url.format) && isFunction(url.concat)) { + /* use UrlMatcher (or compatible object) as is */ + } else if (url != null) { + throw new Error("Invalid url '" + url + "' in state '" + state + "'"); + } + + // Keep track of the closest ancestor state that has a URL (i.e. is navigable) + state.navigable = url ? state : parent ? parent.navigable : null; + + // Derive parameters for this state and ensure they're a super-set of parent's parameters + var params = state.params; + if (params) { + if (!isArray(params)) throw new Error("Invalid params in state '" + state + "'"); + if (url) throw new Error("Both params and url specicified in state '" + state + "'"); + } else { + params = state.params = url ? url.parameters() : state.parent.params; + } + + var paramNames = {}; forEach(params, function (p) { paramNames[p] = true; }); + if (parent) { + forEach(parent.params, function (p) { + if (!paramNames[p]) { + throw new Error("Missing required parameter '" + p + "' in state '" + name + "'"); + } + paramNames[p] = false; + }); + + var ownParams = state.ownParams = []; + forEach(paramNames, function (own, p) { + if (own) ownParams.push(p); + }); + } else { + state.ownParams = params; + } + + // If there is no explicit multi-view configuration, make one up so we don't have + // to handle both cases in the view directive later. Note that having an explicit + // 'views' property will mean the default unnamed view properties are ignored. This + // is also a good time to resolve view names to absolute names, so everything is a + // straight lookup at link time. + var views = {}; + forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) { + if (name.indexOf('@') < 0) name = name + '@' + state.parent.name; + views[name] = view; + }); + state.views = views; + + // Keep a full path from the root down to this state as this is needed for state activation. + state.path = parent ? parent.path.concat(state) : []; // exclude root from path + + // Speed up $state.contains() as it's used a lot + var includes = state.includes = parent ? extend({}, parent.includes) : {}; + includes[name] = true; + + if (!state.resolve) state.resolve = {}; // prevent null checks later + + // Register the state in the global state list and with $urlRouter if necessary. + if (!state['abstract'] && url) { + $urlRouterProvider.when(url, ['$match', function ($match) { + $state.transitionTo(state, $match, false); + }]); + } + states[name] = state; + return state; + } + + // Implicit root state that is always active + root = registerState({ + name: '', + url: '^', + views: null, + 'abstract': true + }); + root.locals = { globals: { $stateParams: {} } }; + root.navigable = null; + + + // .state(state) + // .state(name, state) + this.state = state; + function state(name, definition) { + /*jshint validthis: true */ + if (isObject(name)) definition = name; + else definition.name = name; + registerState(definition); + return this; + } + + // $urlRouter is injected just to ensure it gets instantiated + this.$get = $get; + $get.$inject = ['$rootScope', '$q', '$templateFactory', '$injector', '$stateParams', '$location', '$urlRouter']; + function $get( $rootScope, $q, $templateFactory, $injector, $stateParams, $location, $urlRouter) { + + var TransitionSuperseded = $q.reject(new Error('transition superseded')); + var TransitionPrevented = $q.reject(new Error('transition prevented')); + + $state = { + params: {}, + current: root.self, + $current: root, + transition: null + }; + + // $state.go = function go(to, params) { + // }; + + $state.transitionTo = function transitionTo(to, toParams, updateLocation) { + if (!isDefined(updateLocation)) updateLocation = true; + + to = findState(to); + if (to['abstract']) throw new Error("Cannot transition to abstract state '" + to + "'"); + var toPath = to.path, + from = $state.$current, fromParams = $state.params, fromPath = from.path; + + // Starting from the root of the path, keep all levels that haven't changed + var keep, state, locals = root.locals, toLocals = []; + for (keep = 0, state = toPath[keep]; + state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams); + keep++, state = toPath[keep]) { + locals = toLocals[keep] = state.locals; + } + + // If we're going to the same state and all locals are kept, we've got nothing to do. + // But clear 'transition', as we still want to cancel any other pending transitions. + // TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves, + // because we might accidentally abort a legitimate transition initiated from code? + if (to === from && locals === from.locals) { + $state.transition = null; + return $q.when($state.current); + } + + // Normalize/filter parameters before we pass them to event handlers etc. + toParams = normalize(to.params, toParams || {}); + + // Broadcast start event and cancel the transition if requested + if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams) + .defaultPrevented) return TransitionPrevented; + + // Resolve locals for the remaining states, but don't update any global state just + // yet -- if anything fails to resolve the current state needs to remain untouched. + // We also set up an inheritance chain for the locals here. This allows the view directive + // to quickly look up the correct definition for each view in the current state. Even + // though we create the locals object itself outside resolveState(), it is initially + // empty and gets filled asynchronously. We need to keep track of the promise for the + // (fully resolved) current locals, and pass this down the chain. + var resolved = $q.when(locals); + for (var l=keep; l=keep; l--) { + exiting = fromPath[l]; + if (exiting.self.onExit) { + $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals); + } + exiting.locals = null; + } + + // Enter 'to' states not kept + for (l=keep; l').html(locals.$template).contents(); + animate.enter(contents, element); + } else { + element.html(locals.$template); + contents = element.contents(); + } + + var link = $compile(contents); + viewScope = scope.$new(); + if (locals.$$controller) { + locals.$scope = viewScope; + var controller = $controller(locals.$$controller, locals); + element.children().data('$ngControllerController', controller); + } + link(viewScope); + viewScope.$emit('$viewContentLoaded'); + viewScope.$eval(onloadExp); + + // TODO: This seems strange, shouldn't $anchorScroll listen for $viewContentLoaded if necessary? + // $anchorScroll might listen on event... + $anchorScroll(); + } else { + viewLocals = null; + view.state = null; + } + } + } + }; + return directive; +} + +angular.module('ui.state').directive('uiView', $ViewDirective); + +$RouteProvider.$inject = ['$stateProvider', '$urlRouterProvider']; +function $RouteProvider( $stateProvider, $urlRouterProvider) { + + var routes = []; + + onEnterRoute.$inject = ['$$state']; + function onEnterRoute( $$state) { + /*jshint validthis: true */ + this.locals = $$state.locals.globals; + this.params = this.locals.$stateParams; + } + + function onExitRoute() { + /*jshint validthis: true */ + this.locals = null; + this.params = null; + } + + this.when = when; + function when(url, route) { + /*jshint validthis: true */ + if (route.redirectTo != null) { + // Redirect, configure directly on $urlRouterProvider + var redirect = route.redirectTo, handler; + if (isString(redirect)) { + handler = redirect; // leave $urlRouterProvider to handle + } else if (isFunction(redirect)) { + // Adapt to $urlRouterProvider API + handler = function (params, $location) { + return redirect(params, $location.path(), $location.search()); + }; + } else { + throw new Error("Invalid 'redirectTo' in when()"); + } + $urlRouterProvider.when(url, handler); + } else { + // Regular route, configure as state + $stateProvider.state(inherit(route, { + parent: null, + name: 'route:' + encodeURIComponent(url), + url: url, + onEnter: onEnterRoute, + onExit: onExitRoute + })); + } + routes.push(route); + return this; + } + + this.$get = $get; + $get.$inject = ['$state', '$rootScope', '$routeParams']; + function $get( $state, $rootScope, $routeParams) { + + var $route = { + routes: routes, + params: $routeParams, + current: undefined + }; + + function stateAsRoute(state) { + return (state.name !== '') ? state : undefined; + } + + $rootScope.$on('$stateChangeStart', function (ev, to, toParams, from, fromParams) { + $rootScope.$broadcast('$routeChangeStart', stateAsRoute(to), stateAsRoute(from)); + }); + + $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) { + $route.current = stateAsRoute(to); + $rootScope.$broadcast('$routeChangeSuccess', stateAsRoute(to), stateAsRoute(from)); + copy(toParams, $route.params); + }); + + $rootScope.$on('$stateChangeError', function (ev, to, toParams, from, fromParams, error) { + $rootScope.$broadcast('$routeChangeError', stateAsRoute(to), stateAsRoute(from), error); + }); + + return $route; + } +} + +angular.module('ui.compat') + .provider('$route', $RouteProvider) + .directive('ngView', $ViewDirective); +})(window, window.angular); \ No newline at end of file diff --git a/public/scripts/base/angular.js b/public/scripts/base/angular.js new file mode 100644 index 00000000..5a732aa6 --- /dev/null +++ b/public/scripts/base/angular.js @@ -0,0 +1,16876 @@ +/** + * @license AngularJS v1.1.5 + * (c) 2010-2012 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, document, undefined) { +'use strict'; + +//////////////////////////////////// + +/** + * @ngdoc function + * @name angular.lowercase + * @function + * + * @description Converts the specified string to lowercase. + * @param {string} string String to be converted to lowercase. + * @returns {string} Lowercased string. + */ +var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; + + +/** + * @ngdoc function + * @name angular.uppercase + * @function + * + * @description Converts the specified string to uppercase. + * @param {string} string String to be converted to uppercase. + * @returns {string} Uppercased string. + */ +var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; + + +var manualLowercase = function(s) { + return isString(s) + ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) + : s; +}; +var manualUppercase = function(s) { + return isString(s) + ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) + : s; +}; + + +// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish +// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods +// with correct but slower alternatives. +if ('i' !== 'I'.toLowerCase()) { + lowercase = manualLowercase; + uppercase = manualUppercase; +} + + +var /** holds major version number for IE or NaN for real browsers */ + msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]), + jqLite, // delay binding since jQuery could be loaded after us. + jQuery, // delay binding + slice = [].slice, + push = [].push, + toString = Object.prototype.toString, + + + _angular = window.angular, + /** @name angular */ + angular = window.angular || (window.angular = {}), + angularModule, + nodeName_, + uid = ['0', '0', '0']; + +/** + * @ngdoc function + * @name angular.noConflict + * @function + * + * @description + * Restores the previous global value of angular and returns the current instance. Other libraries may already use the + * angular namespace. Or a previous version of angular is already loaded on the page. In these cases you may want to + * restore the previous namespace and keep a reference to angular. + * + * @return {Object} The current angular namespace + */ +function noConflict() { + var a = window.angular; + window.angular = _angular; + return a; +} + +/** + * @private + * @param {*} obj + * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...) + */ +function isArrayLike(obj) { + if (!obj || (typeof obj.length !== 'number')) return false; + + // We have on object which has length property. Should we treat it as array? + if (typeof obj.hasOwnProperty != 'function' && + typeof obj.constructor != 'function') { + // This is here for IE8: it is a bogus object treat it as array; + return true; + } else { + return obj instanceof JQLite || // JQLite + (jQuery && obj instanceof jQuery) || // jQuery + toString.call(obj) !== '[object Object]' || // some browser native object + typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj) + } +} + +/** + * @ngdoc function + * @name angular.forEach + * @function + * + * @description + * Invokes the `iterator` function once for each item in `obj` collection, which can be either an + * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` + * is the value of an object property or an array element and `key` is the object property key or + * array element index. Specifying a `context` for the function is optional. + * + * Note: this function was previously known as `angular.foreach`. + * +
+     var values = {name: 'misko', gender: 'male'};
+     var log = [];
+     angular.forEach(values, function(value, key){
+       this.push(key + ': ' + value);
+     }, log);
+     expect(log).toEqual(['name: misko', 'gender:male']);
+   
+ * + * @param {Object|Array} obj Object to iterate over. + * @param {Function} iterator Iterator function. + * @param {Object=} context Object to become context (`this`) for the iterator function. + * @returns {Object|Array} Reference to `obj`. + */ +function forEach(obj, iterator, context) { + var key; + if (obj) { + if (isFunction(obj)){ + for (key in obj) { + if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } + } + } else if (obj.forEach && obj.forEach !== forEach) { + obj.forEach(iterator, context); + } else if (isArrayLike(obj)) { + for (key = 0; key < obj.length; key++) + iterator.call(context, obj[key], key); + } else { + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } + } + } + } + return obj; +} + +function sortedKeys(obj) { + var keys = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys.sort(); +} + +function forEachSorted(obj, iterator, context) { + var keys = sortedKeys(obj); + for ( var i = 0; i < keys.length; i++) { + iterator.call(context, obj[keys[i]], keys[i]); + } + return keys; +} + + +/** + * when using forEach the params are value, key, but it is often useful to have key, value. + * @param {function(string, *)} iteratorFn + * @returns {function(*, string)} + */ +function reverseParams(iteratorFn) { + return function(value, key) { iteratorFn(key, value) }; +} + +/** + * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric + * characters such as '012ABC'. The reason why we are not using simply a number counter is that + * the number string gets longer over time, and it can also overflow, where as the nextId + * will grow much slower, it is a string, and it will never overflow. + * + * @returns an unique alpha-numeric string + */ +function nextUid() { + var index = uid.length; + var digit; + + while(index) { + index--; + digit = uid[index].charCodeAt(0); + if (digit == 57 /*'9'*/) { + uid[index] = 'A'; + return uid.join(''); + } + if (digit == 90 /*'Z'*/) { + uid[index] = '0'; + } else { + uid[index] = String.fromCharCode(digit + 1); + return uid.join(''); + } + } + uid.unshift('0'); + return uid.join(''); +} + + +/** + * Set or clear the hashkey for an object. + * @param obj object + * @param h the hashkey (!truthy to delete the hashkey) + */ +function setHashKey(obj, h) { + if (h) { + obj.$$hashKey = h; + } + else { + delete obj.$$hashKey; + } +} + +/** + * @ngdoc function + * @name angular.extend + * @function + * + * @description + * Extends the destination object `dst` by copying all of the properties from the `src` object(s) + * to `dst`. You can specify multiple `src` objects. + * + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @returns {Object} Reference to `dst`. + */ +function extend(dst) { + var h = dst.$$hashKey; + forEach(arguments, function(obj){ + if (obj !== dst) { + forEach(obj, function(value, key){ + dst[key] = value; + }); + } + }); + + setHashKey(dst,h); + return dst; +} + +function int(str) { + return parseInt(str, 10); +} + + +function inherit(parent, extra) { + return extend(new (extend(function() {}, {prototype:parent}))(), extra); +} + +var START_SPACE = /^\s*/; +var END_SPACE = /\s*$/; +function stripWhitespace(str) { + return isString(str) ? str.replace(START_SPACE, '').replace(END_SPACE, '') : str; +} + +/** + * @ngdoc function + * @name angular.noop + * @function + * + * @description + * A function that performs no operations. This function can be useful when writing code in the + * functional style. +
+     function foo(callback) {
+       var result = calculateResult();
+       (callback || angular.noop)(result);
+     }
+   
+ */ +function noop() {} +noop.$inject = []; + + +/** + * @ngdoc function + * @name angular.identity + * @function + * + * @description + * A function that returns its first argument. This function is useful when writing code in the + * functional style. + * +
+     function transformer(transformationFn, value) {
+       return (transformationFn || identity)(value);
+     };
+   
+ */ +function identity($) {return $;} +identity.$inject = []; + + +function valueFn(value) {return function() {return value;};} + +/** + * @ngdoc function + * @name angular.isUndefined + * @function + * + * @description + * Determines if a reference is undefined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is undefined. + */ +function isUndefined(value){return typeof value == 'undefined';} + + +/** + * @ngdoc function + * @name angular.isDefined + * @function + * + * @description + * Determines if a reference is defined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is defined. + */ +function isDefined(value){return typeof value != 'undefined';} + + +/** + * @ngdoc function + * @name angular.isObject + * @function + * + * @description + * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not + * considered to be objects. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Object` but not `null`. + */ +function isObject(value){return value != null && typeof value == 'object';} + + +/** + * @ngdoc function + * @name angular.isString + * @function + * + * @description + * Determines if a reference is a `String`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `String`. + */ +function isString(value){return typeof value == 'string';} + + +/** + * @ngdoc function + * @name angular.isNumber + * @function + * + * @description + * Determines if a reference is a `Number`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Number`. + */ +function isNumber(value){return typeof value == 'number';} + + +/** + * @ngdoc function + * @name angular.isDate + * @function + * + * @description + * Determines if a value is a date. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Date`. + */ +function isDate(value){ + return toString.apply(value) == '[object Date]'; +} + + +/** + * @ngdoc function + * @name angular.isArray + * @function + * + * @description + * Determines if a reference is an `Array`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Array`. + */ +function isArray(value) { + return toString.apply(value) == '[object Array]'; +} + + +/** + * @ngdoc function + * @name angular.isFunction + * @function + * + * @description + * Determines if a reference is a `Function`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Function`. + */ +function isFunction(value){return typeof value == 'function';} + + +/** + * Checks if `obj` is a window object. + * + * @private + * @param {*} obj Object to check + * @returns {boolean} True if `obj` is a window obj. + */ +function isWindow(obj) { + return obj && obj.document && obj.location && obj.alert && obj.setInterval; +} + + +function isScope(obj) { + return obj && obj.$evalAsync && obj.$watch; +} + + +function isFile(obj) { + return toString.apply(obj) === '[object File]'; +} + + +function isBoolean(value) { + return typeof value == 'boolean'; +} + + +function trim(value) { + return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; +} + +/** + * @ngdoc function + * @name angular.isElement + * @function + * + * @description + * Determines if a reference is a DOM element (or wrapped jQuery element). + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). + */ +function isElement(node) { + return node && + (node.nodeName // we are a direct element + || (node.bind && node.find)); // we have a bind and find method part of jQuery API +} + +/** + * @param str 'key1,key2,...' + * @returns {object} in the form of {key1:true, key2:true, ...} + */ +function makeMap(str){ + var obj = {}, items = str.split(","), i; + for ( i = 0; i < items.length; i++ ) + obj[ items[i] ] = true; + return obj; +} + + +if (msie < 9) { + nodeName_ = function(element) { + element = element.nodeName ? element : element[0]; + return (element.scopeName && element.scopeName != 'HTML') + ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; + }; +} else { + nodeName_ = function(element) { + return element.nodeName ? element.nodeName : element[0].nodeName; + }; +} + + +function map(obj, iterator, context) { + var results = []; + forEach(obj, function(value, index, list) { + results.push(iterator.call(context, value, index, list)); + }); + return results; +} + + +/** + * @description + * Determines the number of elements in an array, the number of properties an object has, or + * the length of a string. + * + * Note: This function is used to augment the Object type in Angular expressions. See + * {@link angular.Object} for more information about Angular arrays. + * + * @param {Object|Array|string} obj Object, array, or string to inspect. + * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object + * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. + */ +function size(obj, ownPropsOnly) { + var size = 0, key; + + if (isArray(obj) || isString(obj)) { + return obj.length; + } else if (isObject(obj)){ + for (key in obj) + if (!ownPropsOnly || obj.hasOwnProperty(key)) + size++; + } + + return size; +} + + +function includes(array, obj) { + return indexOf(array, obj) != -1; +} + +function indexOf(array, obj) { + if (array.indexOf) return array.indexOf(obj); + + for ( var i = 0; i < array.length; i++) { + if (obj === array[i]) return i; + } + return -1; +} + +function arrayRemove(array, value) { + var index = indexOf(array, value); + if (index >=0) + array.splice(index, 1); + return value; +} + +function isLeafNode (node) { + if (node) { + switch (node.nodeName) { + case "OPTION": + case "PRE": + case "TITLE": + return true; + } + } + return false; +} + +/** + * @ngdoc function + * @name angular.copy + * @function + * + * @description + * Creates a deep copy of `source`, which should be an object or an array. + * + * * If no destination is supplied, a copy of the object or array is created. + * * If a destination is provided, all of its elements (for array) or properties (for objects) + * are deleted and then all elements/properties from the source are copied to it. + * * If `source` is not an object or array, `source` is returned. + * + * Note: this function is used to augment the Object type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @param {*} source The source that will be used to make a copy. + * Can be any type, including primitives, `null`, and `undefined`. + * @param {(Object|Array)=} destination Destination into which the source is copied. If + * provided, must be of the same type as `source`. + * @returns {*} The copy or updated `destination`, if `destination` was specified. + */ +function copy(source, destination){ + if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope"); + if (!destination) { + destination = source; + if (source) { + if (isArray(source)) { + destination = copy(source, []); + } else if (isDate(source)) { + destination = new Date(source.getTime()); + } else if (isObject(source)) { + destination = copy(source, {}); + } + } + } else { + if (source === destination) throw Error("Can't copy equivalent objects or arrays"); + if (isArray(source)) { + destination.length = 0; + for ( var i = 0; i < source.length; i++) { + destination.push(copy(source[i])); + } + } else { + var h = destination.$$hashKey; + forEach(destination, function(value, key){ + delete destination[key]; + }); + for ( var key in source) { + destination[key] = copy(source[key]); + } + setHashKey(destination,h); + } + } + return destination; +} + +/** + * Create a shallow copy of an object + */ +function shallowCopy(src, dst) { + dst = dst || {}; + + for(var key in src) { + if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') { + dst[key] = src[key]; + } + } + + return dst; +} + + +/** + * @ngdoc function + * @name angular.equals + * @function + * + * @description + * Determines if two objects or two values are equivalent. Supports value types, arrays and + * objects. + * + * Two objects or values are considered equivalent if at least one of the following is true: + * + * * Both objects or values pass `===` comparison. + * * Both objects or values are of the same type and all of their properties pass `===` comparison. + * * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal) + * + * During a property comparison, properties of `function` type and properties with names + * that begin with `$` are ignored. + * + * Scope and DOMWindow objects are being compared only by identify (`===`). + * + * @param {*} o1 Object or value to compare. + * @param {*} o2 Object or value to compare. + * @returns {boolean} True if arguments are equal. + */ +function equals(o1, o2) { + if (o1 === o2) return true; + if (o1 === null || o2 === null) return false; + if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN + var t1 = typeof o1, t2 = typeof o2, length, key, keySet; + if (t1 == t2) { + if (t1 == 'object') { + if (isArray(o1)) { + if ((length = o1.length) == o2.length) { + for(key=0; key 2 ? sliceArgs(arguments, 2) : []; + if (isFunction(fn) && !(fn instanceof RegExp)) { + return curryArgs.length + ? function() { + return arguments.length + ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) + : fn.apply(self, curryArgs); + } + : function() { + return arguments.length + ? fn.apply(self, arguments) + : fn.call(self); + }; + } else { + // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) + return fn; + } +} + + +function toJsonReplacer(key, value) { + var val = value; + + if (/^\$+/.test(key)) { + val = undefined; + } else if (isWindow(value)) { + val = '$WINDOW'; + } else if (value && document === value) { + val = '$DOCUMENT'; + } else if (isScope(value)) { + val = '$SCOPE'; + } + + return val; +} + + +/** + * @ngdoc function + * @name angular.toJson + * @function + * + * @description + * Serializes input into a JSON-formatted string. + * + * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. + * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. + * @returns {string} Jsonified string representing `obj`. + */ +function toJson(obj, pretty) { + return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); +} + + +/** + * @ngdoc function + * @name angular.fromJson + * @function + * + * @description + * Deserializes a JSON string. + * + * @param {string} json JSON string to deserialize. + * @returns {Object|Array|Date|string|number} Deserialized thingy. + */ +function fromJson(json) { + return isString(json) + ? JSON.parse(json) + : json; +} + + +function toBoolean(value) { + if (value && value.length !== 0) { + var v = lowercase("" + value); + value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); + } else { + value = false; + } + return value; +} + +/** + * @returns {string} Returns the string representation of the element. + */ +function startingTag(element) { + element = jqLite(element).clone(); + try { + // turns out IE does not let you set .html() on elements which + // are not allowed to have children. So we just ignore it. + element.html(''); + } catch(e) {} + // As Per DOM Standards + var TEXT_NODE = 3; + var elemHtml = jqLite('
').append(element).html(); + try { + return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) : + elemHtml. + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); + } catch(e) { + return lowercase(elemHtml); + } + +} + + +///////////////////////////////////////////////// + +/** + * Parses an escaped url query string into key-value pairs. + * @returns Object.<(string|boolean)> + */ +function parseKeyValue(/**string*/keyValue) { + var obj = {}, key_value, key; + forEach((keyValue || "").split('&'), function(keyValue){ + if (keyValue) { + key_value = keyValue.split('='); + key = decodeURIComponent(key_value[0]); + obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true; + } + }); + return obj; +} + +function toKeyValue(obj) { + var parts = []; + forEach(obj, function(value, key) { + parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true))); + }); + return parts.length ? parts.join('&') : ''; +} + + +/** + * We need our custom method because encodeURIComponent is too aggressive and doesn't follow + * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path + * segments: + * segment = *pchar + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * pct-encoded = "%" HEXDIG HEXDIG + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriSegment(val) { + return encodeUriQuery(val, true). + replace(/%26/gi, '&'). + replace(/%3D/gi, '='). + replace(/%2B/gi, '+'); +} + + +/** + * This method is intended for encoding *key* or *value* parts of query component. We need a custom + * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be + * encoded per http://tools.ietf.org/html/rfc3986: + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriQuery(val, pctEncodeSpaces) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); +} + + +/** + * @ngdoc directive + * @name ng.directive:ngApp + * + * @element ANY + * @param {angular.Module} ngApp an optional application + * {@link angular.module module} name to load. + * + * @description + * + * Use this directive to auto-bootstrap an application. Only + * one directive can be used per HTML document. The directive + * designates the root of the application and is typically placed + * at the root of the page. + * + * In the example below if the `ngApp` directive would not be placed + * on the `html` element then the document would not be compiled + * and the `{{ 1+2 }}` would not be resolved to `3`. + * + * `ngApp` is the easiest way to bootstrap an application. + * + + + I can add: 1 + 2 = {{ 1+2 }} + + + * + */ +function angularInit(element, bootstrap) { + var elements = [element], + appElement, + module, + names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], + NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; + + function append(element) { + element && elements.push(element); + } + + forEach(names, function(name) { + names[name] = true; + append(document.getElementById(name)); + name = name.replace(':', '\\:'); + if (element.querySelectorAll) { + forEach(element.querySelectorAll('.' + name), append); + forEach(element.querySelectorAll('.' + name + '\\:'), append); + forEach(element.querySelectorAll('[' + name + ']'), append); + } + }); + + forEach(elements, function(element) { + if (!appElement) { + var className = ' ' + element.className + ' '; + var match = NG_APP_CLASS_REGEXP.exec(className); + if (match) { + appElement = element; + module = (match[2] || '').replace(/\s+/g, ','); + } else { + forEach(element.attributes, function(attr) { + if (!appElement && names[attr.name]) { + appElement = element; + module = attr.value; + } + }); + } + } + }); + if (appElement) { + bootstrap(appElement, module ? [module] : []); + } +} + +/** + * @ngdoc function + * @name angular.bootstrap + * @description + * Use this function to manually start up angular application. + * + * See: {@link guide/bootstrap Bootstrap} + * + * @param {Element} element DOM element which is the root of angular application. + * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} + * @returns {AUTO.$injector} Returns the newly created injector for this app. + */ +function bootstrap(element, modules) { + var resumeBootstrapInternal = function() { + element = jqLite(element); + modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); + modules.unshift('ng'); + var injector = createInjector(modules); + injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animator', + function(scope, element, compile, injector, animator) { + scope.$apply(function() { + element.data('$injector', injector); + compile(element)(scope); + }); + animator.enabled(true); + }] + ); + return injector; + }; + + var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; + + if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { + return resumeBootstrapInternal(); + } + + window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); + angular.resumeBootstrap = function(extraModules) { + forEach(extraModules, function(module) { + modules.push(module); + }); + resumeBootstrapInternal(); + }; +} + +var SNAKE_CASE_REGEXP = /[A-Z]/g; +function snake_case(name, separator){ + separator = separator || '_'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); +} + +function bindJQuery() { + // bind to jQuery if present; + jQuery = window.jQuery; + // reset to jQuery or default to us. + if (jQuery) { + jqLite = jQuery; + extend(jQuery.fn, { + scope: JQLitePrototype.scope, + controller: JQLitePrototype.controller, + injector: JQLitePrototype.injector, + inheritedData: JQLitePrototype.inheritedData + }); + JQLitePatchJQueryRemove('remove', true); + JQLitePatchJQueryRemove('empty'); + JQLitePatchJQueryRemove('html'); + } else { + jqLite = JQLite; + } + angular.element = jqLite; +} + +/** + * throw error if the argument is falsy. + */ +function assertArg(arg, name, reason) { + if (!arg) { + throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required")); + } + return arg; +} + +function assertArgFn(arg, name, acceptArrayAnnotation) { + if (acceptArrayAnnotation && isArray(arg)) { + arg = arg[arg.length - 1]; + } + + assertArg(isFunction(arg), name, 'not a function, got ' + + (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg)); + return arg; +} + +/** + * @ngdoc interface + * @name angular.Module + * @description + * + * Interface for configuring angular {@link angular.module modules}. + */ + +function setupModuleLoader(window) { + + function ensure(obj, name, factory) { + return obj[name] || (obj[name] = factory()); + } + + return ensure(ensure(window, 'angular', Object), 'module', function() { + /** @type {Object.} */ + var modules = {}; + + /** + * @ngdoc function + * @name angular.module + * @description + * + * The `angular.module` is a global place for creating and registering Angular modules. All + * modules (angular core or 3rd party) that should be available to an application must be + * registered using this mechanism. + * + * + * # Module + * + * A module is a collocation of services, directives, filters, and configuration information. Module + * is used to configure the {@link AUTO.$injector $injector}. + * + *
+     * // Create a new module
+     * var myModule = angular.module('myModule', []);
+     *
+     * // register a new service
+     * myModule.value('appName', 'MyCoolApp');
+     *
+     * // configure existing services inside initialization blocks.
+     * myModule.config(function($locationProvider) {
+     *   // Configure existing providers
+     *   $locationProvider.hashPrefix('!');
+     * });
+     * 
+ * + * Then you can create an injector and load your modules like this: + * + *
+     * var injector = angular.injector(['ng', 'MyModule'])
+     * 
+ * + * However it's more likely that you'll just use + * {@link ng.directive:ngApp ngApp} or + * {@link angular.bootstrap} to simplify this process for you. + * + * @param {!string} name The name of the module to create or retrieve. + * @param {Array.=} requires If specified then new module is being created. If unspecified then the + * the module is being retrieved for further configuration. + * @param {Function} configFn Optional configuration function for the module. Same as + * {@link angular.Module#config Module#config()}. + * @returns {module} new module with the {@link angular.Module} api. + */ + return function module(name, requires, configFn) { + if (requires && modules.hasOwnProperty(name)) { + modules[name] = null; + } + return ensure(modules, name, function() { + if (!requires) { + throw Error('No module: ' + name); + } + + /** @type {!Array.>} */ + var invokeQueue = []; + + /** @type {!Array.} */ + var runBlocks = []; + + var config = invokeLater('$injector', 'invoke'); + + /** @type {angular.Module} */ + var moduleInstance = { + // Private state + _invokeQueue: invokeQueue, + _runBlocks: runBlocks, + + /** + * @ngdoc property + * @name angular.Module#requires + * @propertyOf angular.Module + * @returns {Array.} List of module names which must be loaded before this module. + * @description + * Holds the list of modules which the injector will load before the current module is loaded. + */ + requires: requires, + + /** + * @ngdoc property + * @name angular.Module#name + * @propertyOf angular.Module + * @returns {string} Name of the module. + * @description + */ + name: name, + + + /** + * @ngdoc method + * @name angular.Module#provider + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the service. + * @description + * See {@link AUTO.$provide#provider $provide.provider()}. + */ + provider: invokeLater('$provide', 'provider'), + + /** + * @ngdoc method + * @name angular.Module#factory + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. + * @description + * See {@link AUTO.$provide#factory $provide.factory()}. + */ + factory: invokeLater('$provide', 'factory'), + + /** + * @ngdoc method + * @name angular.Module#service + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} constructor A constructor function that will be instantiated. + * @description + * See {@link AUTO.$provide#service $provide.service()}. + */ + service: invokeLater('$provide', 'service'), + + /** + * @ngdoc method + * @name angular.Module#value + * @methodOf angular.Module + * @param {string} name service name + * @param {*} object Service instance object. + * @description + * See {@link AUTO.$provide#value $provide.value()}. + */ + value: invokeLater('$provide', 'value'), + + /** + * @ngdoc method + * @name angular.Module#constant + * @methodOf angular.Module + * @param {string} name constant name + * @param {*} object Constant value. + * @description + * Because the constant are fixed, they get applied before other provide methods. + * See {@link AUTO.$provide#constant $provide.constant()}. + */ + constant: invokeLater('$provide', 'constant', 'unshift'), + + /** + * @ngdoc method + * @name angular.Module#animation + * @methodOf angular.Module + * @param {string} name animation name + * @param {Function} animationFactory Factory function for creating new instance of an animation. + * @description + * + * Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate} + * alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives. + *
+           * module.animation('animation-name', function($inject1, $inject2) {
+           *   return {
+           *     //this gets called in preparation to setup an animation
+           *     setup : function(element) { ... },
+           *
+           *     //this gets called once the animation is run
+           *     start : function(element, done, memo) { ... }
+           *   }
+           * })
+           * 
+ * + * See {@link ng.$animationProvider#register $animationProvider.register()} and + * {@link ng.directive:ngAnimate ngAnimate} for more information. + */ + animation: invokeLater('$animationProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#filter + * @methodOf angular.Module + * @param {string} name Filter name. + * @param {Function} filterFactory Factory function for creating new instance of filter. + * @description + * See {@link ng.$filterProvider#register $filterProvider.register()}. + */ + filter: invokeLater('$filterProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#controller + * @methodOf angular.Module + * @param {string} name Controller name. + * @param {Function} constructor Controller constructor function. + * @description + * See {@link ng.$controllerProvider#register $controllerProvider.register()}. + */ + controller: invokeLater('$controllerProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#directive + * @methodOf angular.Module + * @param {string} name directive name + * @param {Function} directiveFactory Factory function for creating new instance of + * directives. + * @description + * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + directive: invokeLater('$compileProvider', 'directive'), + + /** + * @ngdoc method + * @name angular.Module#config + * @methodOf angular.Module + * @param {Function} configFn Execute this function on module load. Useful for service + * configuration. + * @description + * Use this method to register work which needs to be performed on module loading. + */ + config: config, + + /** + * @ngdoc method + * @name angular.Module#run + * @methodOf angular.Module + * @param {Function} initializationFn Execute this function after injector creation. + * Useful for application initialization. + * @description + * Use this method to register work which should be performed when the injector is done + * loading all modules. + */ + run: function(block) { + runBlocks.push(block); + return this; + } + }; + + if (configFn) { + config(configFn); + } + + return moduleInstance; + + /** + * @param {string} provider + * @param {string} method + * @param {String=} insertMethod + * @returns {angular.Module} + */ + function invokeLater(provider, method, insertMethod) { + return function() { + invokeQueue[insertMethod || 'push']([provider, method, arguments]); + return moduleInstance; + } + } + }); + }; + }); + +} + +/** + * @ngdoc property + * @name angular.version + * @description + * An object that contains information about the current AngularJS version. This object has the + * following properties: + * + * - `full` – `{string}` – Full version string, such as "0.9.18". + * - `major` – `{number}` – Major version number, such as "0". + * - `minor` – `{number}` – Minor version number, such as "9". + * - `dot` – `{number}` – Dot version number, such as "18". + * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". + */ +var version = { + full: '1.1.5', // all of these placeholder strings will be replaced by grunt's + major: 1, // package task + minor: 1, + dot: 5, + codeName: 'triangle-squarification' +}; + + +function publishExternalAPI(angular){ + extend(angular, { + 'bootstrap': bootstrap, + 'copy': copy, + 'extend': extend, + 'equals': equals, + 'element': jqLite, + 'forEach': forEach, + 'injector': createInjector, + 'noop':noop, + 'bind':bind, + 'toJson': toJson, + 'fromJson': fromJson, + 'identity':identity, + 'isUndefined': isUndefined, + 'isDefined': isDefined, + 'isString': isString, + 'isFunction': isFunction, + 'isObject': isObject, + 'isNumber': isNumber, + 'isElement': isElement, + 'isArray': isArray, + 'version': version, + 'isDate': isDate, + 'lowercase': lowercase, + 'uppercase': uppercase, + 'callbacks': {counter: 0}, + 'noConflict': noConflict + }); + + angularModule = setupModuleLoader(window); + try { + angularModule('ngLocale'); + } catch (e) { + angularModule('ngLocale', []).provider('$locale', $LocaleProvider); + } + + angularModule('ng', ['ngLocale'], ['$provide', + function ngModule($provide) { + $provide.provider('$compile', $CompileProvider). + directive({ + a: htmlAnchorDirective, + input: inputDirective, + textarea: inputDirective, + form: formDirective, + script: scriptDirective, + select: selectDirective, + style: styleDirective, + option: optionDirective, + ngBind: ngBindDirective, + ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective, + ngBindTemplate: ngBindTemplateDirective, + ngClass: ngClassDirective, + ngClassEven: ngClassEvenDirective, + ngClassOdd: ngClassOddDirective, + ngCsp: ngCspDirective, + ngCloak: ngCloakDirective, + ngController: ngControllerDirective, + ngForm: ngFormDirective, + ngHide: ngHideDirective, + ngIf: ngIfDirective, + ngInclude: ngIncludeDirective, + ngInit: ngInitDirective, + ngNonBindable: ngNonBindableDirective, + ngPluralize: ngPluralizeDirective, + ngRepeat: ngRepeatDirective, + ngShow: ngShowDirective, + ngSubmit: ngSubmitDirective, + ngStyle: ngStyleDirective, + ngSwitch: ngSwitchDirective, + ngSwitchWhen: ngSwitchWhenDirective, + ngSwitchDefault: ngSwitchDefaultDirective, + ngOptions: ngOptionsDirective, + ngView: ngViewDirective, + ngTransclude: ngTranscludeDirective, + ngModel: ngModelDirective, + ngList: ngListDirective, + ngChange: ngChangeDirective, + required: requiredDirective, + ngRequired: requiredDirective, + ngValue: ngValueDirective + }). + directive(ngAttributeAliasDirectives). + directive(ngEventDirectives); + $provide.provider({ + $anchorScroll: $AnchorScrollProvider, + $animation: $AnimationProvider, + $animator: $AnimatorProvider, + $browser: $BrowserProvider, + $cacheFactory: $CacheFactoryProvider, + $controller: $ControllerProvider, + $document: $DocumentProvider, + $exceptionHandler: $ExceptionHandlerProvider, + $filter: $FilterProvider, + $interpolate: $InterpolateProvider, + $http: $HttpProvider, + $httpBackend: $HttpBackendProvider, + $location: $LocationProvider, + $log: $LogProvider, + $parse: $ParseProvider, + $route: $RouteProvider, + $routeParams: $RouteParamsProvider, + $rootScope: $RootScopeProvider, + $q: $QProvider, + $sniffer: $SnifferProvider, + $templateCache: $TemplateCacheProvider, + $timeout: $TimeoutProvider, + $window: $WindowProvider + }); + } + ]); +} + +////////////////////////////////// +//JQLite +////////////////////////////////// + +/** + * @ngdoc function + * @name angular.element + * @function + * + * @description + * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. + * `angular.element` can be either an alias for [jQuery](http://api.jquery.com/jQuery/) function, if + * jQuery is available, or a function that wraps the element or string in Angular's jQuery lite + * implementation (commonly referred to as jqLite). + * + * Real jQuery always takes precedence over jqLite, provided it was loaded before `DOMContentLoaded` + * event fired. + * + * jqLite is a tiny, API-compatible subset of jQuery that allows + * Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality + * within a very small footprint, so only a subset of the jQuery API - methods, arguments and + * invocation styles - are supported. + * + * Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never + * raw DOM references. + * + * ## Angular's jQuery lite provides the following methods: + * + * - [addClass()](http://api.jquery.com/addClass/) + * - [after()](http://api.jquery.com/after/) + * - [append()](http://api.jquery.com/append/) + * - [attr()](http://api.jquery.com/attr/) + * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces + * - [children()](http://api.jquery.com/children/) - Does not support selectors + * - [clone()](http://api.jquery.com/clone/) + * - [contents()](http://api.jquery.com/contents/) + * - [css()](http://api.jquery.com/css/) + * - [data()](http://api.jquery.com/data/) + * - [eq()](http://api.jquery.com/eq/) + * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name + * - [hasClass()](http://api.jquery.com/hasClass/) + * - [html()](http://api.jquery.com/html/) + * - [next()](http://api.jquery.com/next/) - Does not support selectors + * - [parent()](http://api.jquery.com/parent/) - Does not support selectors + * - [prepend()](http://api.jquery.com/prepend/) + * - [prop()](http://api.jquery.com/prop/) + * - [ready()](http://api.jquery.com/ready/) + * - [remove()](http://api.jquery.com/remove/) + * - [removeAttr()](http://api.jquery.com/removeAttr/) + * - [removeClass()](http://api.jquery.com/removeClass/) + * - [removeData()](http://api.jquery.com/removeData/) + * - [replaceWith()](http://api.jquery.com/replaceWith/) + * - [text()](http://api.jquery.com/text/) + * - [toggleClass()](http://api.jquery.com/toggleClass/) + * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. + * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces + * - [val()](http://api.jquery.com/val/) + * - [wrap()](http://api.jquery.com/wrap/) + * + * ## In addition to the above, Angular provides additional methods to both jQuery and jQuery lite: + * + * - `controller(name)` - retrieves the controller of the current element or its parent. By default + * retrieves controller associated with the `ngController` directive. If `name` is provided as + * camelCase directive name, then the controller for this directive will be retrieved (e.g. + * `'ngModel'`). + * - `injector()` - retrieves the injector of the current element or its parent. + * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current + * element or its parent. + * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top + * parent element is reached. + * + * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. + * @returns {Object} jQuery object. + */ + +var jqCache = JQLite.cache = {}, + jqName = JQLite.expando = 'ng-' + new Date().getTime(), + jqId = 1, + addEventListenerFn = (window.document.addEventListener + ? function(element, type, fn) {element.addEventListener(type, fn, false);} + : function(element, type, fn) {element.attachEvent('on' + type, fn);}), + removeEventListenerFn = (window.document.removeEventListener + ? function(element, type, fn) {element.removeEventListener(type, fn, false); } + : function(element, type, fn) {element.detachEvent('on' + type, fn); }); + +function jqNextId() { return ++jqId; } + + +var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; +var MOZ_HACK_REGEXP = /^moz([A-Z])/; + +/** + * Converts snake_case to camelCase. + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function camelCase(name) { + return name. + replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }). + replace(MOZ_HACK_REGEXP, 'Moz$1'); +} + +///////////////////////////////////////////// +// jQuery mutation patch +// +// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a +// $destroy event on all DOM nodes being removed. +// +///////////////////////////////////////////// + +function JQLitePatchJQueryRemove(name, dispatchThis) { + var originalJqFn = jQuery.fn[name]; + originalJqFn = originalJqFn.$original || originalJqFn; + removePatch.$original = originalJqFn; + jQuery.fn[name] = removePatch; + + function removePatch() { + var list = [this], + fireEvent = dispatchThis, + set, setIndex, setLength, + element, childIndex, childLength, children, + fns, events; + + while(list.length) { + set = list.shift(); + for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { + element = jqLite(set[setIndex]); + if (fireEvent) { + element.triggerHandler('$destroy'); + } else { + fireEvent = !fireEvent; + } + for(childIndex = 0, childLength = (children = element.children()).length; + childIndex < childLength; + childIndex++) { + list.push(jQuery(children[childIndex])); + } + } + } + return originalJqFn.apply(this, arguments); + } +} + +///////////////////////////////////////////// +function JQLite(element) { + if (element instanceof JQLite) { + return element; + } + if (!(this instanceof JQLite)) { + if (isString(element) && element.charAt(0) != '<') { + throw Error('selectors not implemented'); + } + return new JQLite(element); + } + + if (isString(element)) { + var div = document.createElement('div'); + // Read about the NoScope elements here: + // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx + div.innerHTML = '
 
' + element; // IE insanity to make NoScope elements work! + div.removeChild(div.firstChild); // remove the superfluous div + JQLiteAddNodes(this, div.childNodes); + this.remove(); // detach the elements from the temporary DOM div. + } else { + JQLiteAddNodes(this, element); + } +} + +function JQLiteClone(element) { + return element.cloneNode(true); +} + +function JQLiteDealoc(element){ + JQLiteRemoveData(element); + for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { + JQLiteDealoc(children[i]); + } +} + +function JQLiteUnbind(element, type, fn) { + var events = JQLiteExpandoStore(element, 'events'), + handle = JQLiteExpandoStore(element, 'handle'); + + if (!handle) return; //no listeners registered + + if (isUndefined(type)) { + forEach(events, function(eventHandler, type) { + removeEventListenerFn(element, type, eventHandler); + delete events[type]; + }); + } else { + if (isUndefined(fn)) { + removeEventListenerFn(element, type, events[type]); + delete events[type]; + } else { + arrayRemove(events[type], fn); + } + } +} + +function JQLiteRemoveData(element) { + var expandoId = element[jqName], + expandoStore = jqCache[expandoId]; + + if (expandoStore) { + if (expandoStore.handle) { + expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); + JQLiteUnbind(element); + } + delete jqCache[expandoId]; + element[jqName] = undefined; // ie does not allow deletion of attributes on elements. + } +} + +function JQLiteExpandoStore(element, key, value) { + var expandoId = element[jqName], + expandoStore = jqCache[expandoId || -1]; + + if (isDefined(value)) { + if (!expandoStore) { + element[jqName] = expandoId = jqNextId(); + expandoStore = jqCache[expandoId] = {}; + } + expandoStore[key] = value; + } else { + return expandoStore && expandoStore[key]; + } +} + +function JQLiteData(element, key, value) { + var data = JQLiteExpandoStore(element, 'data'), + isSetter = isDefined(value), + keyDefined = !isSetter && isDefined(key), + isSimpleGetter = keyDefined && !isObject(key); + + if (!data && !isSimpleGetter) { + JQLiteExpandoStore(element, 'data', data = {}); + } + + if (isSetter) { + data[key] = value; + } else { + if (keyDefined) { + if (isSimpleGetter) { + // don't create data in this case. + return data && data[key]; + } else { + extend(data, key); + } + } else { + return data; + } + } +} + +function JQLiteHasClass(element, selector) { + return ((" " + element.className + " ").replace(/[\n\t]/g, " "). + indexOf( " " + selector + " " ) > -1); +} + +function JQLiteRemoveClass(element, cssClasses) { + if (cssClasses) { + forEach(cssClasses.split(' '), function(cssClass) { + element.className = trim( + (" " + element.className + " ") + .replace(/[\n\t]/g, " ") + .replace(" " + trim(cssClass) + " ", " ") + ); + }); + } +} + +function JQLiteAddClass(element, cssClasses) { + if (cssClasses) { + forEach(cssClasses.split(' '), function(cssClass) { + if (!JQLiteHasClass(element, cssClass)) { + element.className = trim(element.className + ' ' + trim(cssClass)); + } + }); + } +} + +function JQLiteAddNodes(root, elements) { + if (elements) { + elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) + ? elements + : [ elements ]; + for(var i=0; i < elements.length; i++) { + root.push(elements[i]); + } + } +} + +function JQLiteController(element, name) { + return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); +} + +function JQLiteInheritedData(element, name, value) { + element = jqLite(element); + + // if element is the document object work with the html element instead + // this makes $(document).scope() possible + if(element[0].nodeType == 9) { + element = element.find('html'); + } + + while (element.length) { + if (value = element.data(name)) return value; + element = element.parent(); + } +} + +////////////////////////////////////////// +// Functions which are declared directly. +////////////////////////////////////////// +var JQLitePrototype = JQLite.prototype = { + ready: function(fn) { + var fired = false; + + function trigger() { + if (fired) return; + fired = true; + fn(); + } + + // check if document already is loaded + if (document.readyState === 'complete'){ + setTimeout(trigger); + } else { + this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9 + // we can not use jqLite since we are not done loading and jQuery could be loaded later. + JQLite(window).bind('load', trigger); // fallback to window.onload for others + } + }, + toString: function() { + var value = []; + forEach(this, function(e){ value.push('' + e);}); + return '[' + value.join(', ') + ']'; + }, + + eq: function(index) { + return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); + }, + + length: 0, + push: push, + sort: [].sort, + splice: [].splice +}; + +////////////////////////////////////////// +// Functions iterating getter/setters. +// these functions return self on setter and +// value on get. +////////////////////////////////////////// +var BOOLEAN_ATTR = {}; +forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { + BOOLEAN_ATTR[lowercase(value)] = value; +}); +var BOOLEAN_ELEMENTS = {}; +forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { + BOOLEAN_ELEMENTS[uppercase(value)] = true; +}); + +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; +} + +forEach({ + data: JQLiteData, + inheritedData: JQLiteInheritedData, + + scope: function(element) { + return JQLiteInheritedData(element, '$scope'); + }, + + controller: JQLiteController , + + injector: function(element) { + return JQLiteInheritedData(element, '$injector'); + }, + + removeAttr: function(element,name) { + element.removeAttribute(name); + }, + + hasClass: JQLiteHasClass, + + css: function(element, name, value) { + name = camelCase(name); + + if (isDefined(value)) { + element.style[name] = value; + } else { + var val; + + if (msie <= 8) { + // this is some IE specific weirdness that jQuery 1.6.4 does not sure why + val = element.currentStyle && element.currentStyle[name]; + if (val === '') val = 'auto'; + } + + val = val || element.style[name]; + + if (msie <= 8) { + // jquery weirdness :-/ + val = (val === '') ? undefined : val; + } + + return val; + } + }, + + attr: function(element, name, value){ + var lowercasedName = lowercase(name); + if (BOOLEAN_ATTR[lowercasedName]) { + if (isDefined(value)) { + if (!!value) { + element[name] = true; + element.setAttribute(name, lowercasedName); + } else { + element[name] = false; + element.removeAttribute(lowercasedName); + } + } else { + return (element[name] || + (element.attributes.getNamedItem(name)|| noop).specified) + ? lowercasedName + : undefined; + } + } else if (isDefined(value)) { + element.setAttribute(name, value); + } else if (element.getAttribute) { + // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code + // some elements (e.g. Document) don't have get attribute, so return undefined + var ret = element.getAttribute(name, 2); + // normalize non-existing attributes to undefined (as jQuery) + return ret === null ? undefined : ret; + } + }, + + prop: function(element, name, value) { + if (isDefined(value)) { + element[name] = value; + } else { + return element[name]; + } + }, + + text: extend((msie < 9) + ? function(element, value) { + if (element.nodeType == 1 /** Element */) { + if (isUndefined(value)) + return element.innerText; + element.innerText = value; + } else { + if (isUndefined(value)) + return element.nodeValue; + element.nodeValue = value; + } + } + : function(element, value) { + if (isUndefined(value)) { + return element.textContent; + } + element.textContent = value; + }, {$dv:''}), + + val: function(element, value) { + if (isUndefined(value)) { + return element.value; + } + element.value = value; + }, + + html: function(element, value) { + if (isUndefined(value)) { + return element.innerHTML; + } + for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { + JQLiteDealoc(childNodes[i]); + } + element.innerHTML = value; + } +}, function(fn, name){ + /** + * Properties: writes return selection, reads return first value + */ + JQLite.prototype[name] = function(arg1, arg2) { + var i, key; + + // JQLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it + // in a way that survives minification. + if (((fn.length == 2 && (fn !== JQLiteHasClass && fn !== JQLiteController)) ? arg1 : arg2) === undefined) { + if (isObject(arg1)) { + + // we are a write, but the object properties are the key/values + for(i=0; i < this.length; i++) { + if (fn === JQLiteData) { + // data() takes the whole object in jQuery + fn(this[i], arg1); + } else { + for (key in arg1) { + fn(this[i], key, arg1[key]); + } + } + } + // return self for chaining + return this; + } else { + // we are a read, so read the first child. + if (this.length) + return fn(this[0], arg1, arg2); + } + } else { + // we are a write, so apply to all children + for(i=0; i < this.length; i++) { + fn(this[i], arg1, arg2); + } + // return self for chaining + return this; + } + return fn.$dv; + }; +}); + +function createEventHandler(element, events) { + var eventHandler = function (event, type) { + if (!event.preventDefault) { + event.preventDefault = function() { + event.returnValue = false; //ie + }; + } + + if (!event.stopPropagation) { + event.stopPropagation = function() { + event.cancelBubble = true; //ie + }; + } + + if (!event.target) { + event.target = event.srcElement || document; + } + + if (isUndefined(event.defaultPrevented)) { + var prevent = event.preventDefault; + event.preventDefault = function() { + event.defaultPrevented = true; + prevent.call(event); + }; + event.defaultPrevented = false; + } + + event.isDefaultPrevented = function() { + return event.defaultPrevented || event.returnValue == false; + }; + + forEach(events[type || event.type], function(fn) { + fn.call(element, event); + }); + + // Remove monkey-patched methods (IE), + // as they would cause memory leaks in IE8. + if (msie <= 8) { + // IE7/8 does not allow to delete property on native object + event.preventDefault = null; + event.stopPropagation = null; + event.isDefaultPrevented = null; + } else { + // It shouldn't affect normal browsers (native methods are defined on prototype). + delete event.preventDefault; + delete event.stopPropagation; + delete event.isDefaultPrevented; + } + }; + eventHandler.elem = element; + return eventHandler; +} + +////////////////////////////////////////// +// Functions iterating traversal. +// These functions chain results into a single +// selector. +////////////////////////////////////////// +forEach({ + removeData: JQLiteRemoveData, + + dealoc: JQLiteDealoc, + + bind: function bindFn(element, type, fn){ + var events = JQLiteExpandoStore(element, 'events'), + handle = JQLiteExpandoStore(element, 'handle'); + + if (!events) JQLiteExpandoStore(element, 'events', events = {}); + if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); + + forEach(type.split(' '), function(type){ + var eventFns = events[type]; + + if (!eventFns) { + if (type == 'mouseenter' || type == 'mouseleave') { + var contains = document.body.contains || document.body.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + events[type] = []; + + // Refer to jQuery's implementation of mouseenter & mouseleave + // Read about mouseenter and mouseleave: + // http://www.quirksmode.org/js/events_mouse.html#link8 + var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"} + bindFn(element, eventmap[type], function(event) { + var ret, target = this, related = event.relatedTarget; + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !contains(target, related)) ){ + handle(event, type); + } + + }); + + } else { + addEventListenerFn(element, type, handle); + events[type] = []; + } + eventFns = events[type] + } + eventFns.push(fn); + }); + }, + + unbind: JQLiteUnbind, + + replaceWith: function(element, replaceNode) { + var index, parent = element.parentNode; + JQLiteDealoc(element); + forEach(new JQLite(replaceNode), function(node){ + if (index) { + parent.insertBefore(node, index.nextSibling); + } else { + parent.replaceChild(node, element); + } + index = node; + }); + }, + + children: function(element) { + var children = []; + forEach(element.childNodes, function(element){ + if (element.nodeType === 1) + children.push(element); + }); + return children; + }, + + contents: function(element) { + return element.childNodes || []; + }, + + append: function(element, node) { + forEach(new JQLite(node), function(child){ + if (element.nodeType === 1 || element.nodeType === 11) { + element.appendChild(child); + } + }); + }, + + prepend: function(element, node) { + if (element.nodeType === 1) { + var index = element.firstChild; + forEach(new JQLite(node), function(child){ + if (index) { + element.insertBefore(child, index); + } else { + element.appendChild(child); + index = child; + } + }); + } + }, + + wrap: function(element, wrapNode) { + wrapNode = jqLite(wrapNode)[0]; + var parent = element.parentNode; + if (parent) { + parent.replaceChild(wrapNode, element); + } + wrapNode.appendChild(element); + }, + + remove: function(element) { + JQLiteDealoc(element); + var parent = element.parentNode; + if (parent) parent.removeChild(element); + }, + + after: function(element, newElement) { + var index = element, parent = element.parentNode; + forEach(new JQLite(newElement), function(node){ + parent.insertBefore(node, index.nextSibling); + index = node; + }); + }, + + addClass: JQLiteAddClass, + removeClass: JQLiteRemoveClass, + + toggleClass: function(element, selector, condition) { + if (isUndefined(condition)) { + condition = !JQLiteHasClass(element, selector); + } + (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector); + }, + + parent: function(element) { + var parent = element.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + + next: function(element) { + if (element.nextElementSibling) { + return element.nextElementSibling; + } + + // IE8 doesn't have nextElementSibling + var elm = element.nextSibling; + while (elm != null && elm.nodeType !== 1) { + elm = elm.nextSibling; + } + return elm; + }, + + find: function(element, selector) { + return element.getElementsByTagName(selector); + }, + + clone: JQLiteClone, + + triggerHandler: function(element, eventName) { + var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; + var event; + + forEach(eventFns, function(fn) { + fn.call(element, {preventDefault: noop}); + }); + } +}, function(fn, name){ + /** + * chaining functions + */ + JQLite.prototype[name] = function(arg1, arg2) { + var value; + for(var i=0; i < this.length; i++) { + if (value == undefined) { + value = fn(this[i], arg1, arg2); + if (value !== undefined) { + // any function which returns a value needs to be wrapped + value = jqLite(value); + } + } else { + JQLiteAddNodes(value, fn(this[i], arg1, arg2)); + } + } + return value == undefined ? this : value; + }; +}); + +/** + * Computes a hash of an 'obj'. + * Hash of a: + * string is string + * number is number as string + * object is either result of calling $$hashKey function on the object or uniquely generated id, + * that is also assigned to the $$hashKey property of the object. + * + * @param obj + * @returns {string} hash string such that the same input will have the same hash string. + * The resulting string key is in 'type:hashKey' format. + */ +function hashKey(obj) { + var objType = typeof obj, + key; + + if (objType == 'object' && obj !== null) { + if (typeof (key = obj.$$hashKey) == 'function') { + // must invoke on object to keep the right this + key = obj.$$hashKey(); + } else if (key === undefined) { + key = obj.$$hashKey = nextUid(); + } + } else { + key = obj; + } + + return objType + ':' + key; +} + +/** + * HashMap which can use objects as keys + */ +function HashMap(array){ + forEach(array, this.put, this); +} +HashMap.prototype = { + /** + * Store key value pair + * @param key key to store can be any type + * @param value value to store can be any type + */ + put: function(key, value) { + this[hashKey(key)] = value; + }, + + /** + * @param key + * @returns the value for the key + */ + get: function(key) { + return this[hashKey(key)]; + }, + + /** + * Remove the key/value pair + * @param key + */ + remove: function(key) { + var value = this[key = hashKey(key)]; + delete this[key]; + return value; + } +}; + +/** + * @ngdoc function + * @name angular.injector + * @function + * + * @description + * Creates an injector function that can be used for retrieving services as well as for + * dependency injection (see {@link guide/di dependency injection}). + * + + * @param {Array.} modules A list of module functions or their aliases. See + * {@link angular.module}. The `ng` module must be explicitly added. + * @returns {function()} Injector function. See {@link AUTO.$injector $injector}. + * + * @example + * Typical usage + *
+ *   // create an injector
+ *   var $injector = angular.injector(['ng']);
+ *
+ *   // use the injector to kick off your application
+ *   // use the type inference to auto inject arguments, or use implicit injection
+ *   $injector.invoke(function($rootScope, $compile, $document){
+ *     $compile($document)($rootScope);
+ *     $rootScope.$digest();
+ *   });
+ * 
+ */ + + +/** + * @ngdoc overview + * @name AUTO + * @description + * + * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}. + */ + +var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; +var FN_ARG_SPLIT = /,/; +var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; +var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; +function annotate(fn) { + var $inject, + fnText, + argDecl, + last; + + if (typeof fn == 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + fnText = fn.toString().replace(STRIP_COMMENTS, ''); + argDecl = fnText.match(FN_ARGS); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ + arg.replace(FN_ARG, function(all, underscore, name){ + $inject.push(name); + }); + }); + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn'); + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); + } + return $inject; +} + +/////////////////////////////////////// + +/** + * @ngdoc object + * @name AUTO.$injector + * @function + * + * @description + * + * `$injector` is used to retrieve object instances as defined by + * {@link AUTO.$provide provider}, instantiate types, invoke methods, + * and load modules. + * + * The following always holds true: + * + *
+ *   var $injector = angular.injector();
+ *   expect($injector.get('$injector')).toBe($injector);
+ *   expect($injector.invoke(function($injector){
+ *     return $injector;
+ *   }).toBe($injector);
+ * 
+ * + * # Injection Function Annotation + * + * JavaScript does not have annotations, and annotations are needed for dependency injection. The + * following are all valid ways of annotating function with injection arguments and are equivalent. + * + *
+ *   // inferred (only works if code not minified/obfuscated)
+ *   $injector.invoke(function(serviceA){});
+ *
+ *   // annotated
+ *   function explicit(serviceA) {};
+ *   explicit.$inject = ['serviceA'];
+ *   $injector.invoke(explicit);
+ *
+ *   // inline
+ *   $injector.invoke(['serviceA', function(serviceA){}]);
+ * 
+ * + * ## Inference + * + * In JavaScript calling `toString()` on a function returns the function definition. The definition can then be + * parsed and the function arguments can be extracted. *NOTE:* This does not work with minification, and obfuscation + * tools since these tools change the argument names. + * + * ## `$inject` Annotation + * By adding a `$inject` property onto a function the injection parameters can be specified. + * + * ## Inline + * As an array of injection names, where the last item in the array is the function to call. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#get + * @methodOf AUTO.$injector + * + * @description + * Return an instance of the service. + * + * @param {string} name The name of the instance to retrieve. + * @return {*} The instance. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#invoke + * @methodOf AUTO.$injector + * + * @description + * Invoke the method and supply the method arguments from the `$injector`. + * + * @param {!function} fn The function to invoke. The function arguments come form the function annotation. + * @param {Object=} self The `this` for the invoked method. + * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before + * the `$injector` is consulted. + * @returns {*} the value returned by the invoked `fn` function. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#has + * @methodOf AUTO.$injector + * + * @description + * Allows the user to query if the particular service exist. + * + * @param {string} Name of the service to query. + * @returns {boolean} returns true if injector has given service. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#instantiate + * @methodOf AUTO.$injector + * @description + * Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies + * all of the arguments to the constructor function as specified by the constructor annotation. + * + * @param {function} Type Annotated constructor function. + * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before + * the `$injector` is consulted. + * @returns {Object} new instance of `Type`. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#annotate + * @methodOf AUTO.$injector + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is used by the injector + * to determine which services need to be injected into the function when the function is invoked. There are three + * ways in which the function can be annotated with the needed dependencies. + * + * # Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting + * the function into a string using `toString()` method and extracting the argument names. + *
+ *   // Given
+ *   function MyController($scope, $route) {
+ *     // ...
+ *   }
+ *
+ *   // Then
+ *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+ * 
+ * + * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies + * are supported. + * + * # The `$inject` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of + * services to be injected into the function. + *
+ *   // Given
+ *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
+ *     // ...
+ *   }
+ *   // Define function dependencies
+ *   MyController.$inject = ['$scope', '$route'];
+ *
+ *   // Then
+ *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+ * 
+ * + * # The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very + * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives + * minification is a better choice: + * + *
+ *   // We wish to write this (not minification / obfuscation safe)
+ *   injector.invoke(function($compile, $rootScope) {
+ *     // ...
+ *   });
+ *
+ *   // We are forced to write break inlining
+ *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
+ *     // ...
+ *   };
+ *   tmpFn.$inject = ['$compile', '$rootScope'];
+ *   injector.invoke(tmpFn);
+ *
+ *   // To better support inline function the inline annotation is supported
+ *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
+ *     // ...
+ *   }]);
+ *
+ *   // Therefore
+ *   expect(injector.annotate(
+ *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
+ *    ).toEqual(['$compile', '$rootScope']);
+ * 
+ * + * @param {function|Array.} fn Function for which dependent service names need to be retrieved as described + * above. + * + * @returns {Array.} The names of the services which the function requires. + */ + + + + +/** + * @ngdoc object + * @name AUTO.$provide + * + * @description + * + * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance. + * The providers share the same name as the instance they create with `Provider` suffixed to them. + * + * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of + * a service. The Provider can have additional methods which would allow for configuration of the provider. + * + *
+ *   function GreetProvider() {
+ *     var salutation = 'Hello';
+ *
+ *     this.salutation = function(text) {
+ *       salutation = text;
+ *     };
+ *
+ *     this.$get = function() {
+ *       return function (name) {
+ *         return salutation + ' ' + name + '!';
+ *       };
+ *     };
+ *   }
+ *
+ *   describe('Greeter', function(){
+ *
+ *     beforeEach(module(function($provide) {
+ *       $provide.provider('greet', GreetProvider);
+ *     }));
+ *
+ *     it('should greet', inject(function(greet) {
+ *       expect(greet('angular')).toEqual('Hello angular!');
+ *     }));
+ *
+ *     it('should allow configuration of salutation', function() {
+ *       module(function(greetProvider) {
+ *         greetProvider.salutation('Ahoj');
+ *       });
+ *       inject(function(greet) {
+ *         expect(greet('angular')).toEqual('Ahoj angular!');
+ *       });
+ *     });
+ * 
+ */ + +/** + * @ngdoc method + * @name AUTO.$provide#provider + * @methodOf AUTO.$provide + * @description + * + * Register a provider for a service. The providers can be retrieved and can have additional configuration methods. + * + * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key. + * @param {(Object|function())} provider If the provider is: + * + * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using + * {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created. + * - `Constructor`: a new instance of the provider will be created using + * {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`. + * + * @returns {Object} registered provider instance + */ + +/** + * @ngdoc method + * @name AUTO.$provide#factory + * @methodOf AUTO.$provide + * @description + * + * A short hand for configuring services if only `$get` method is required. + * + * @param {string} name The name of the instance. + * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for + * `$provide.provider(name, {$get: $getFn})`. + * @returns {Object} registered provider instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#service + * @methodOf AUTO.$provide + * @description + * + * A short hand for registering service of given class. + * + * @param {string} name The name of the instance. + * @param {Function} constructor A class (constructor function) that will be instantiated. + * @returns {Object} registered provider instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#value + * @methodOf AUTO.$provide + * @description + * + * A short hand for configuring services if the `$get` method is a constant. + * + * @param {string} name The name of the instance. + * @param {*} value The value. + * @returns {Object} registered provider instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#constant + * @methodOf AUTO.$provide + * @description + * + * A constant value, but unlike {@link AUTO.$provide#value value} it can be injected + * into configuration function (other modules) and it is not interceptable by + * {@link AUTO.$provide#decorator decorator}. + * + * @param {string} name The name of the constant. + * @param {*} value The constant value. + * @returns {Object} registered instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#decorator + * @methodOf AUTO.$provide + * @description + * + * Decoration of service, allows the decorator to intercept the service instance creation. The + * returned instance may be the original instance, or a new instance which delegates to the + * original instance. + * + * @param {string} name The name of the service to decorate. + * @param {function()} decorator This function will be invoked when the service needs to be + * instantiated. The function is called using the {@link AUTO.$injector#invoke + * injector.invoke} method and is therefore fully injectable. Local injection arguments: + * + * * `$delegate` - The original service instance, which can be monkey patched, configured, + * decorated or delegated to. + */ + + +function createInjector(modulesToLoad) { + var INSTANTIATING = {}, + providerSuffix = 'Provider', + path = [], + loadedModules = new HashMap(), + providerCache = { + $provide: { + provider: supportObject(provider), + factory: supportObject(factory), + service: supportObject(service), + value: supportObject(value), + constant: supportObject(constant), + decorator: decorator + } + }, + providerInjector = (providerCache.$injector = + createInternalInjector(providerCache, function() { + throw Error("Unknown provider: " + path.join(' <- ')); + })), + instanceCache = {}, + instanceInjector = (instanceCache.$injector = + createInternalInjector(instanceCache, function(servicename) { + var provider = providerInjector.get(servicename + providerSuffix); + return instanceInjector.invoke(provider.$get, provider); + })); + + + forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); + + return instanceInjector; + + //////////////////////////////////// + // $provider + //////////////////////////////////// + + function supportObject(delegate) { + return function(key, value) { + if (isObject(key)) { + forEach(key, reverseParams(delegate)); + } else { + return delegate(key, value); + } + } + } + + function provider(name, provider_) { + if (isFunction(provider_) || isArray(provider_)) { + provider_ = providerInjector.instantiate(provider_); + } + if (!provider_.$get) { + throw Error('Provider ' + name + ' must define $get factory method.'); + } + return providerCache[name + providerSuffix] = provider_; + } + + function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } + + function service(name, constructor) { + return factory(name, ['$injector', function($injector) { + return $injector.instantiate(constructor); + }]); + } + + function value(name, value) { return factory(name, valueFn(value)); } + + function constant(name, value) { + providerCache[name] = value; + instanceCache[name] = value; + } + + function decorator(serviceName, decorFn) { + var origProvider = providerInjector.get(serviceName + providerSuffix), + orig$get = origProvider.$get; + + origProvider.$get = function() { + var origInstance = instanceInjector.invoke(orig$get, origProvider); + return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); + }; + } + + //////////////////////////////////// + // Module Loading + //////////////////////////////////// + function loadModules(modulesToLoad){ + var runBlocks = []; + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.put(module, true); + if (isString(module)) { + var moduleFn = angularModule(module); + runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); + + try { + for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { + var invokeArgs = invokeQueue[i], + provider = providerInjector.get(invokeArgs[0]); + + provider[invokeArgs[1]].apply(provider, invokeArgs[2]); + } + } catch (e) { + if (e.message) e.message += ' from ' + module; + throw e; + } + } else if (isFunction(module)) { + try { + runBlocks.push(providerInjector.invoke(module)); + } catch (e) { + if (e.message) e.message += ' from ' + module; + throw e; + } + } else if (isArray(module)) { + try { + runBlocks.push(providerInjector.invoke(module)); + } catch (e) { + if (e.message) e.message += ' from ' + String(module[module.length - 1]); + throw e; + } + } else { + assertArgFn(module, 'module'); + } + }); + return runBlocks; + } + + //////////////////////////////////// + // internal Injector + //////////////////////////////////// + + function createInternalInjector(cache, factory) { + + function getService(serviceName) { + if (typeof serviceName !== 'string') { + throw Error('Service name expected'); + } + if (cache.hasOwnProperty(serviceName)) { + if (cache[serviceName] === INSTANTIATING) { + throw Error('Circular dependency: ' + path.join(' <- ')); + } + return cache[serviceName]; + } else { + try { + path.unshift(serviceName); + cache[serviceName] = INSTANTIATING; + return cache[serviceName] = factory(serviceName); + } finally { + path.shift(); + } + } + } + + function invoke(fn, self, locals){ + var args = [], + $inject = annotate(fn), + length, i, + key; + + for(i = 0, length = $inject.length; i < length; i++) { + key = $inject[i]; + args.push( + locals && locals.hasOwnProperty(key) + ? locals[key] + : getService(key) + ); + } + if (!fn.$inject) { + // this means that we must be an array. + fn = fn[length]; + } + + + // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke + switch (self ? -1 : args.length) { + case 0: return fn(); + case 1: return fn(args[0]); + case 2: return fn(args[0], args[1]); + case 3: return fn(args[0], args[1], args[2]); + case 4: return fn(args[0], args[1], args[2], args[3]); + case 5: return fn(args[0], args[1], args[2], args[3], args[4]); + case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); + default: return fn.apply(self, args); + } + } + + function instantiate(Type, locals) { + var Constructor = function() {}, + instance, returnedValue; + + // Check if Type is annotated and use just the given function at n-1 as parameter + // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); + Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; + instance = new Constructor(); + returnedValue = invoke(Type, instance, locals); + + return isObject(returnedValue) ? returnedValue : instance; + } + + return { + invoke: invoke, + instantiate: instantiate, + get: getService, + annotate: annotate, + has: function(name) { + return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); + } + }; + } +} + +/** + * @ngdoc function + * @name ng.$anchorScroll + * @requires $window + * @requires $location + * @requires $rootScope + * + * @description + * When called, it checks current value of `$location.hash()` and scroll to related element, + * according to rules specified in + * {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}. + * + * It also watches the `$location.hash()` and scroll whenever it changes to match any anchor. + * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. + */ +function $AnchorScrollProvider() { + + var autoScrollingEnabled = true; + + this.disableAutoScrolling = function() { + autoScrollingEnabled = false; + }; + + this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { + var document = $window.document; + + // helper function to get first anchor from a NodeList + // can't use filter.filter, as it accepts only instances of Array + // and IE can't convert NodeList to an array using [].slice + // TODO(vojta): use filter if we change it to accept lists as well + function getFirstAnchor(list) { + var result = null; + forEach(list, function(element) { + if (!result && lowercase(element.nodeName) === 'a') result = element; + }); + return result; + } + + function scroll() { + var hash = $location.hash(), elm; + + // empty hash, scroll to the top of the page + if (!hash) $window.scrollTo(0, 0); + + // element with given id + else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); + + // first anchor with given name :-D + else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); + + // no element and hash == 'top', scroll to the top of the page + else if (hash === 'top') $window.scrollTo(0, 0); + } + + // does not scroll when user clicks on anchor link that is currently on + // (no url change, no $location.hash() change), browser native does scroll + if (autoScrollingEnabled) { + $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, + function autoScrollWatchAction() { + $rootScope.$evalAsync(scroll); + }); + } + + return scroll; + }]; +} + + +/** + * @ngdoc object + * @name ng.$animationProvider + * @description + * + * The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside + * of a module. + * + */ +$AnimationProvider.$inject = ['$provide']; +function $AnimationProvider($provide) { + var suffix = 'Animation'; + + /** + * @ngdoc function + * @name ng.$animation#register + * @methodOf ng.$animationProvider + * + * @description + * Registers a new injectable animation factory function. The factory function produces the animation object which + * has these two properties: + * + * * `setup`: `function(Element):*` A function which receives the starting state of the element. The purpose + * of this function is to get the element ready for animation. Optionally the function returns an memento which + * is passed to the `start` function. + * * `start`: `function(Element, doneFunction, *)` The element to animate, the `doneFunction` to be called on + * element animation completion, and an optional memento from the `setup` function. + * + * @param {string} name The name of the animation. + * @param {function} factory The factory function that will be executed to return the animation object. + * + */ + this.register = function(name, factory) { + $provide.factory(camelCase(name) + suffix, factory); + }; + + this.$get = ['$injector', function($injector) { + /** + * @ngdoc function + * @name ng.$animation + * @function + * + * @description + * The $animation service is used to retrieve any defined animation functions. When executed, the $animation service + * will return a object that contains the setup and start functions that were defined for the animation. + * + * @param {String} name Name of the animation function to retrieve. Animation functions are registered and stored + * inside of the AngularJS DI so a call to $animate('custom') is the same as injecting `customAnimation` + * via dependency injection. + * @return {Object} the animation object which contains the `setup` and `start` functions that perform the animation. + */ + return function $animation(name) { + if (name) { + var animationName = camelCase(name) + suffix; + if ($injector.has(animationName)) { + return $injector.get(animationName); + } + } + }; + }]; +} + +// NOTE: this is a pseudo directive. + +/** + * @ngdoc directive + * @name ng.directive:ngAnimate + * + * @description + * The `ngAnimate` directive works as an attribute that is attached alongside pre-existing directives. + * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place + * without burdening the directive which uses the animation with animation details. The built in directives + * `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView` already accept `ngAnimate` directive. + * Custom directives can take advantage of animation through {@link ng.$animator $animator service}. + * + * Below is a more detailed breakdown of the supported callback events provided by pre-exisitng ng directives: + * + * | Directive | Supported Animations | + * |========================================================== |====================================================| + * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move | + * | {@link ng.directive:ngView#animations ngView} | enter and leave | + * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | + * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | + * | {@link ng.directive:ngIf#animations ngIf} | enter and leave | + * | {@link ng.directive:ngShow#animations ngShow & ngHide} | show and hide | + * + * You can find out more information about animations upon visiting each directive page. + * + * Below is an example of a directive that makes use of the ngAnimate attribute: + * + *
+ * 
+ * 
+ *
+ * 
+ * 
+ * 
+ * 
+ *
+ * 
+ * 
+ * 
+ * + * The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned. + * + * Keep in mind that if an animation is running, no child element of such animation can also be animated. + * + *

CSS-defined Animations

+ * By default, ngAnimate attaches two CSS classes per animation event to the DOM element to achieve the animation. + * It is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions as + * well as CSS animations. + * + * The following code below demonstrates how to perform animations using **CSS transitions** with ngAnimate: + * + *
+ * 
+ *
+ * 
+ *
+ * + * The following code below demonstrates how to perform animations using **CSS animations** with ngAnimate: + * + *
+ * 
+ *
+ * 
+ *
+ * + * ngAnimate will first examine any CSS animation code and then fallback to using CSS transitions. + * + * Upon DOM mutation, the event class is added first, then the browser is allowed to reflow the content and then, + * the active class is added to trigger the animation. The ngAnimate directive will automatically extract the duration + * of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be + * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end + * immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element + * has no CSS transition/animation classes surrounding it. + * + *

JavaScript-defined Animations

+ * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations to browsers that do not + * yet support them, then you can make use of JavaScript animations defined inside of your AngularJS module. + * + *
+ * var ngModule = angular.module('YourApp', []);
+ * ngModule.animation('animate-enter', function() {
+ *   return {
+ *     setup : function(element) {
+ *       //prepare the element for animation
+ *       element.css({ 'opacity': 0 });
+ *       var memo = "..."; //this value is passed to the start function
+ *       return memo;
+ *     },
+ *     start : function(element, done, memo) {
+ *       //start the animation
+ *       element.animate({
+ *         'opacity' : 1
+ *       }, function() {
+ *         //call when the animation is complete
+ *         done()
+ *       });
+ *     }
+ *   }
+ * });
+ * 
+ * + * As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation + * can be used in the same way with the ngAnimate attribute. Keep in mind that, when using JavaScript-enabled + * animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're not using + * CSS animations) to animated the element, but it will not attempt to find any CSS3 transition or animation duration/delay values. + * It will instead close off the animation once the provided done function is executed. So it's important that you + * make sure your animations remember to fire off the done function once the animations are complete. + * + * @param {expression} ngAnimate Used to configure the DOM manipulation animations. + * + */ + +var $AnimatorProvider = function() { + var NG_ANIMATE_CONTROLLER = '$ngAnimateController'; + var rootAnimateController = {running:true}; + + this.$get = ['$animation', '$window', '$sniffer', '$rootElement', '$rootScope', + function($animation, $window, $sniffer, $rootElement, $rootScope) { + $rootElement.data(NG_ANIMATE_CONTROLLER, rootAnimateController); + + /** + * @ngdoc function + * @name ng.$animator + * @function + * + * @description + * The $animator.create service provides the DOM manipulation API which is decorated with animations. + * + * @param {Scope} scope the scope for the ng-animate. + * @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are + * passed into the linking function of the directive using the `$animator`.) + * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods. + */ + var AnimatorService = function(scope, attrs) { + var animator = {}; + + /** + * @ngdoc function + * @name ng.animator#enter + * @methodOf ng.$animator + * @function + * + * @description + * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation. + * + * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation + * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation + * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation + */ + animator.enter = animateActionFactory('enter', insert, noop); + + /** + * @ngdoc function + * @name ng.animator#leave + * @methodOf ng.$animator + * @function + * + * @description + * Runs the leave animation operation and, upon completion, removes the element from the DOM. + * + * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation + * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation + */ + animator.leave = animateActionFactory('leave', noop, remove); + + /** + * @ngdoc function + * @name ng.animator#move + * @methodOf ng.$animator + * @function + * + * @description + * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or + * add the element directly after the after element if present. Then the move animation will be run. + * + * @param {jQuery/jqLite element} element the element that will be the focus of the move animation + * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation + * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation + */ + animator.move = animateActionFactory('move', move, noop); + + /** + * @ngdoc function + * @name ng.animator#show + * @methodOf ng.$animator + * @function + * + * @description + * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after. + * + * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden + */ + animator.show = animateActionFactory('show', show, noop); + + /** + * @ngdoc function + * @name ng.animator#hide + * @methodOf ng.$animator + * + * @description + * Starts the hide animation first and sets the CSS `display` property to `none` upon completion. + * + * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden + */ + animator.hide = animateActionFactory('hide', noop, hide); + + /** + * @ngdoc function + * @name ng.animator#animate + * @methodOf ng.$animator + * + * @description + * Triggers a custom animation event to be executed on the given element + * + * @param {jQuery/jqLite element} element that will be animated + */ + animator.animate = function(event, element) { + animateActionFactory(event, noop, noop)(element); + } + return animator; + + function animateActionFactory(type, beforeFn, afterFn) { + return function(element, parent, after) { + var ngAnimateValue = scope.$eval(attrs.ngAnimate); + var className = ngAnimateValue + ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type + : ''; + var animationPolyfill = $animation(className); + var polyfillSetup = animationPolyfill && animationPolyfill.setup; + var polyfillStart = animationPolyfill && animationPolyfill.start; + var polyfillCancel = animationPolyfill && animationPolyfill.cancel; + + if (!className) { + beforeFn(element, parent, after); + afterFn(element, parent, after); + } else { + var activeClassName = className + '-active'; + + if (!parent) { + parent = after ? after.parent() : element.parent(); + } + if ((!$sniffer.transitions && !polyfillSetup && !polyfillStart) || + (parent.inheritedData(NG_ANIMATE_CONTROLLER) || noop).running) { + beforeFn(element, parent, after); + afterFn(element, parent, after); + return; + } + + var animationData = element.data(NG_ANIMATE_CONTROLLER) || {}; + if(animationData.running) { + (polyfillCancel || noop)(element); + animationData.done(); + } + + element.data(NG_ANIMATE_CONTROLLER, {running:true, done:done}); + element.addClass(className); + beforeFn(element, parent, after); + if (element.length == 0) return done(); + + var memento = (polyfillSetup || noop)(element); + + // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate + // keep at 1 for animation dom rerender + $window.setTimeout(beginAnimation, 1); + } + + function parseMaxTime(str) { + var total = 0, values = isString(str) ? str.split(/\s*,\s*/) : []; + forEach(values, function(value) { + total = Math.max(parseFloat(value) || 0, total); + }); + return total; + } + + function beginAnimation() { + element.addClass(activeClassName); + if (polyfillStart) { + polyfillStart(element, done, memento); + } else if (isFunction($window.getComputedStyle)) { + //one day all browsers will have these properties + var w3cAnimationProp = 'animation'; + var w3cTransitionProp = 'transition'; + + //but some still use vendor-prefixed styles + var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation'; + var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition'; + + var durationKey = 'Duration', + delayKey = 'Delay', + animationIterationCountKey = 'IterationCount', + duration = 0; + + //we want all the styles defined before and after + var ELEMENT_NODE = 1; + forEach(element, function(element) { + if (element.nodeType == ELEMENT_NODE) { + var w3cProp = w3cTransitionProp, + vendorProp = vendorTransitionProp, + iterations = 1, + elementStyles = $window.getComputedStyle(element) || {}; + + //use CSS Animations over CSS Transitions + if(parseFloat(elementStyles[w3cAnimationProp + durationKey]) > 0 || + parseFloat(elementStyles[vendorAnimationProp + durationKey]) > 0) { + w3cProp = w3cAnimationProp; + vendorProp = vendorAnimationProp; + iterations = Math.max(parseInt(elementStyles[w3cProp + animationIterationCountKey]) || 0, + parseInt(elementStyles[vendorProp + animationIterationCountKey]) || 0, + iterations); + } + + var parsedDelay = Math.max(parseMaxTime(elementStyles[w3cProp + delayKey]), + parseMaxTime(elementStyles[vendorProp + delayKey])); + + var parsedDuration = Math.max(parseMaxTime(elementStyles[w3cProp + durationKey]), + parseMaxTime(elementStyles[vendorProp + durationKey])); + + duration = Math.max(parsedDelay + (iterations * parsedDuration), duration); + } + }); + $window.setTimeout(done, duration * 1000); + } else { + done(); + } + } + + function done() { + if(!done.run) { + done.run = true; + afterFn(element, parent, after); + element.removeClass(className); + element.removeClass(activeClassName); + element.removeData(NG_ANIMATE_CONTROLLER); + } + } + }; + } + + function show(element) { + element.css('display', ''); + } + + function hide(element) { + element.css('display', 'none'); + } + + function insert(element, parent, after) { + if (after) { + after.after(element); + } else { + parent.append(element); + } + } + + function remove(element) { + element.remove(); + } + + function move(element, parent, after) { + // Do not remove element before insert. Removing will cause data associated with the + // element to be dropped. Insert will implicitly do the remove. + insert(element, parent, after); + } + }; + + /** + * @ngdoc function + * @name ng.animator#enabled + * @methodOf ng.$animator + * @function + * + * @param {Boolean=} If provided then set the animation on or off. + * @return {Boolean} Current animation state. + * + * @description + * Globally enables/disables animations. + * + */ + AnimatorService.enabled = function(value) { + if (arguments.length) { + rootAnimateController.running = !value; + } + return !rootAnimateController.running; + }; + + return AnimatorService; + }]; +}; + +/** + * ! This is a private undocumented service ! + * + * @name ng.$browser + * @requires $log + * @description + * This object has two goals: + * + * - hide all the global state in the browser caused by the window object + * - abstract away all the browser specific features and inconsistencies + * + * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` + * service, which can be used for convenient testing of the application without the interaction with + * the real browser apis. + */ +/** + * @param {object} window The global window object. + * @param {object} document jQuery wrapped document. + * @param {function()} XHR XMLHttpRequest constructor. + * @param {object} $log console.log or an object with the same interface. + * @param {object} $sniffer $sniffer service + */ +function Browser(window, document, $log, $sniffer) { + var self = this, + rawDocument = document[0], + location = window.location, + history = window.history, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + pendingDeferIds = {}; + + self.isMock = false; + + var outstandingRequestCount = 0; + var outstandingRequestCallbacks = []; + + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = completeOutstandingRequest; + self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; + + /** + * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` + * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. + */ + function completeOutstandingRequest(fn) { + try { + fn.apply(null, sliceArgs(arguments, 1)); + } finally { + outstandingRequestCount--; + if (outstandingRequestCount === 0) { + while(outstandingRequestCallbacks.length) { + try { + outstandingRequestCallbacks.pop()(); + } catch (e) { + $log.error(e); + } + } + } + } + } + + /** + * @private + * Note: this method is used only by scenario runner + * TODO(vojta): prefix this method with $$ ? + * @param {function()} callback Function that will be called when no outstanding request + */ + self.notifyWhenNoOutstandingRequests = function(callback) { + // force browser to execute all pollFns - this is needed so that cookies and other pollers fire + // at some deterministic time in respect to the test runner's actions. Leaving things up to the + // regular poller would result in flaky tests. + forEach(pollFns, function(pollFn){ pollFn(); }); + + if (outstandingRequestCount === 0) { + callback(); + } else { + outstandingRequestCallbacks.push(callback); + } + }; + + ////////////////////////////////////////////////////////////// + // Poll Watcher API + ////////////////////////////////////////////////////////////// + var pollFns = [], + pollTimeout; + + /** + * @name ng.$browser#addPollFn + * @methodOf ng.$browser + * + * @param {function()} fn Poll function to add + * + * @description + * Adds a function to the list of functions that poller periodically executes, + * and starts polling if not started yet. + * + * @returns {function()} the added function + */ + self.addPollFn = function(fn) { + if (isUndefined(pollTimeout)) startPoller(100, setTimeout); + pollFns.push(fn); + return fn; + }; + + /** + * @param {number} interval How often should browser call poll functions (ms) + * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. + * + * @description + * Configures the poller to run in the specified intervals, using the specified + * setTimeout fn and kicks it off. + */ + function startPoller(interval, setTimeout) { + (function check() { + forEach(pollFns, function(pollFn){ pollFn(); }); + pollTimeout = setTimeout(check, interval); + })(); + } + + ////////////////////////////////////////////////////////////// + // URL API + ////////////////////////////////////////////////////////////// + + var lastBrowserUrl = location.href, + baseElement = document.find('base'); + + /** + * @name ng.$browser#url + * @methodOf ng.$browser + * + * @description + * GETTER: + * Without any argument, this method just returns current value of location.href. + * + * SETTER: + * With at least one argument, this method sets url to new value. + * If html5 history api supported, pushState/replaceState is used, otherwise + * location.href/location.replace is used. + * Returns its own instance to allow chaining + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to change url. + * + * @param {string} url New url (when used as setter) + * @param {boolean=} replace Should new url replace current history record ? + */ + self.url = function(url, replace) { + // setter + if (url) { + if (lastBrowserUrl == url) return; + lastBrowserUrl = url; + if ($sniffer.history) { + if (replace) history.replaceState(null, '', url); + else { + history.pushState(null, '', url); + // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 + baseElement.attr('href', baseElement.attr('href')); + } + } else { + if (replace) location.replace(url); + else location.href = url; + } + return self; + // getter + } else { + // the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 + return location.href.replace(/%27/g,"'"); + } + }; + + var urlChangeListeners = [], + urlChangeInit = false; + + function fireUrlChange() { + if (lastBrowserUrl == self.url()) return; + + lastBrowserUrl = self.url(); + forEach(urlChangeListeners, function(listener) { + listener(self.url()); + }); + } + + /** + * @name ng.$browser#onUrlChange + * @methodOf ng.$browser + * @TODO(vojta): refactor to use node's syntax for events + * + * @description + * Register callback function that will be called, when url changes. + * + * It's only called when the url is changed by outside of angular: + * - user types different url into address bar + * - user clicks on history (forward/back) button + * - user clicks on a link + * + * It's not called when url is changed by $browser.url() method + * + * The listener gets called with new url as parameter. + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to monitor url changes in angular apps. + * + * @param {function(string)} listener Listener function to be called when url changes. + * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. + */ + self.onUrlChange = function(callback) { + if (!urlChangeInit) { + // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) + // don't fire popstate when user change the address bar and don't fire hashchange when url + // changed by push/replaceState + + // html5 history api - popstate event + if ($sniffer.history) jqLite(window).bind('popstate', fireUrlChange); + // hashchange event + if ($sniffer.hashchange) jqLite(window).bind('hashchange', fireUrlChange); + // polling + else self.addPollFn(fireUrlChange); + + urlChangeInit = true; + } + + urlChangeListeners.push(callback); + return callback; + }; + + ////////////////////////////////////////////////////////////// + // Misc API + ////////////////////////////////////////////////////////////// + + /** + * Returns current + * (always relative - without domain) + * + * @returns {string=} + */ + self.baseHref = function() { + var href = baseElement.attr('href'); + return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : ''; + }; + + ////////////////////////////////////////////////////////////// + // Cookies API + ////////////////////////////////////////////////////////////// + var lastCookies = {}; + var lastCookieString = ''; + var cookiePath = self.baseHref(); + + /** + * @name ng.$browser#cookies + * @methodOf ng.$browser + * + * @param {string=} name Cookie name + * @param {string=} value Cookie value + * + * @description + * The cookies method provides a 'private' low level access to browser cookies. + * It is not meant to be used directly, use the $cookie service instead. + * + * The return values vary depending on the arguments that the method was called with as follows: + *
    + *
  • cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it
  • + *
  • cookies(name, value) -> set name to value, if value is undefined delete the cookie
  • + *
  • cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)
  • + *
+ * + * @returns {Object} Hash of all cookies (if called without any parameter) + */ + self.cookies = function(name, value) { + var cookieLength, cookieArray, cookie, i, index; + + if (name) { + if (value === undefined) { + rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; + } else { + if (isString(value)) { + cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1; + + // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: + // - 300 cookies + // - 20 cookies per unique domain + // - 4096 bytes per cookie + if (cookieLength > 4096) { + $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+ + cookieLength + " > 4096 bytes)!"); + } + } + } + } else { + if (rawDocument.cookie !== lastCookieString) { + lastCookieString = rawDocument.cookie; + cookieArray = lastCookieString.split("; "); + lastCookies = {}; + + for (i = 0; i < cookieArray.length; i++) { + cookie = cookieArray[i]; + index = cookie.indexOf('='); + if (index > 0) { //ignore nameless cookies + var name = unescape(cookie.substring(0, index)); + // the first value that is seen for a cookie is the most + // specific one. values for the same cookie name that + // follow are for less specific paths. + if (lastCookies[name] === undefined) { + lastCookies[name] = unescape(cookie.substring(index + 1)); + } + } + } + } + return lastCookies; + } + }; + + + /** + * @name ng.$browser#defer + * @methodOf ng.$browser + * @param {function()} fn A function, who's execution should be defered. + * @param {number=} [delay=0] of milliseconds to defer the function execution. + * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. + * + * @description + * Executes a fn asynchronously via `setTimeout(fn, delay)`. + * + * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using + * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed + * via `$browser.defer.flush()`. + * + */ + self.defer = function(fn, delay) { + var timeoutId; + outstandingRequestCount++; + timeoutId = setTimeout(function() { + delete pendingDeferIds[timeoutId]; + completeOutstandingRequest(fn); + }, delay || 0); + pendingDeferIds[timeoutId] = true; + return timeoutId; + }; + + + /** + * @name ng.$browser#defer.cancel + * @methodOf ng.$browser.defer + * + * @description + * Cancels a defered task identified with `deferId`. + * + * @param {*} deferId Token returned by the `$browser.defer` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully canceled. + */ + self.defer.cancel = function(deferId) { + if (pendingDeferIds[deferId]) { + delete pendingDeferIds[deferId]; + clearTimeout(deferId); + completeOutstandingRequest(noop); + return true; + } + return false; + }; + +} + +function $BrowserProvider(){ + this.$get = ['$window', '$log', '$sniffer', '$document', + function( $window, $log, $sniffer, $document){ + return new Browser($window, $document, $log, $sniffer); + }]; +} + +/** + * @ngdoc object + * @name ng.$cacheFactory + * + * @description + * Factory that constructs cache objects. + * + * + * @param {string} cacheId Name or id of the newly created cache. + * @param {object=} options Options object that specifies the cache behavior. Properties: + * + * - `{number=}` `capacity` — turns the cache into LRU cache. + * + * @returns {object} Newly created cache object with the following set of methods: + * + * - `{object}` `info()` — Returns id, size, and options of cache. + * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it. + * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. + * - `{void}` `removeAll()` — Removes all cached values. + * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. + * + */ +function $CacheFactoryProvider() { + + this.$get = function() { + var caches = {}; + + function cacheFactory(cacheId, options) { + if (cacheId in caches) { + throw Error('cacheId ' + cacheId + ' taken'); + } + + var size = 0, + stats = extend({}, options, {id: cacheId}), + data = {}, + capacity = (options && options.capacity) || Number.MAX_VALUE, + lruHash = {}, + freshEnd = null, + staleEnd = null; + + return caches[cacheId] = { + + put: function(key, value) { + var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); + + refresh(lruEntry); + + if (isUndefined(value)) return; + if (!(key in data)) size++; + data[key] = value; + + if (size > capacity) { + this.remove(staleEnd.key); + } + + return value; + }, + + + get: function(key) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + refresh(lruEntry); + + return data[key]; + }, + + + remove: function(key) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + if (lruEntry == freshEnd) freshEnd = lruEntry.p; + if (lruEntry == staleEnd) staleEnd = lruEntry.n; + link(lruEntry.n,lruEntry.p); + + delete lruHash[key]; + delete data[key]; + size--; + }, + + + removeAll: function() { + data = {}; + size = 0; + lruHash = {}; + freshEnd = staleEnd = null; + }, + + + destroy: function() { + data = null; + stats = null; + lruHash = null; + delete caches[cacheId]; + }, + + + info: function() { + return extend({}, stats, {size: size}); + } + }; + + + /** + * makes the `entry` the freshEnd of the LRU linked list + */ + function refresh(entry) { + if (entry != freshEnd) { + if (!staleEnd) { + staleEnd = entry; + } else if (staleEnd == entry) { + staleEnd = entry.n; + } + + link(entry.n, entry.p); + link(entry, freshEnd); + freshEnd = entry; + freshEnd.n = null; + } + } + + + /** + * bidirectionally links two entries of the LRU linked list + */ + function link(nextEntry, prevEntry) { + if (nextEntry != prevEntry) { + if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify + if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify + } + } + } + + + cacheFactory.info = function() { + var info = {}; + forEach(caches, function(cache, cacheId) { + info[cacheId] = cache.info(); + }); + return info; + }; + + + cacheFactory.get = function(cacheId) { + return caches[cacheId]; + }; + + + return cacheFactory; + }; +} + +/** + * @ngdoc object + * @name ng.$templateCache + * + * @description + * Cache used for storing html templates. + * + * See {@link ng.$cacheFactory $cacheFactory}. + * + */ +function $TemplateCacheProvider() { + this.$get = ['$cacheFactory', function($cacheFactory) { + return $cacheFactory('templates'); + }]; +} + +/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! + * + * DOM-related variables: + * + * - "node" - DOM Node + * - "element" - DOM Element or Node + * - "$node" or "$element" - jqLite-wrapped node or element + * + * + * Compiler related stuff: + * + * - "linkFn" - linking fn of a single directive + * - "nodeLinkFn" - function that aggregates all linking fns for a particular node + * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node + * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) + */ + + +var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; + + +/** + * @ngdoc function + * @name ng.$compile + * @function + * + * @description + * Compiles a piece of HTML string or DOM into a template and produces a template function, which + * can then be used to link {@link ng.$rootScope.Scope scope} and the template together. + * + * The compilation is a process of walking the DOM tree and trying to match DOM elements to + * {@link ng.$compileProvider#directive directives}. For each match it + * executes corresponding template function and collects the + * instance functions into a single template function which is then returned. + * + * The template function can then be used once to produce the view or as it is the case with + * {@link ng.directive:ngRepeat repeater} many-times, in which + * case each call results in a view that is a DOM clone of the original template. + * + + + +
+
+
+
+
+
+ + it('should auto compile', function() { + expect(element('div[compile]').text()).toBe('Hello Angular'); + input('html').enter('{{name}}!'); + expect(element('div[compile]').text()).toBe('Angular!'); + }); + +
+ + * + * + * @param {string|DOMElement} element Element or HTML string to compile into a template function. + * @param {function(angular.Scope[, cloneAttachFn]} transclude function available to directives. + * @param {number} maxPriority only apply directives lower then given priority (Only effects the + * root element(s), not their children) + * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template + * (a DOM element/tree) to a scope. Where: + * + * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. + * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the + * `template` and call the `cloneAttachFn` function allowing the caller to attach the + * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is + * called as:
`cloneAttachFn(clonedElement, scope)` where: + * + * * `clonedElement` - is a clone of the original `element` passed into the compiler. + * * `scope` - is the current scope with which the linking function is working with. + * + * Calling the linking function returns the element of the template. It is either the original element + * passed in, or the clone of the element if the `cloneAttachFn` is provided. + * + * After linking the view is not updated until after a call to $digest which typically is done by + * Angular automatically. + * + * If you need access to the bound view, there are two ways to do it: + * + * - If you are not asking the linking function to clone the template, create the DOM element(s) + * before you send them to the compiler and keep this reference around. + *
+ *     var element = $compile('

{{total}}

')(scope); + *
+ * + * - if on the other hand, you need the element to be cloned, the view reference from the original + * example would not point to the clone, but rather to the original template that was cloned. In + * this case, you can access the clone via the cloneAttachFn: + *
+ *     var templateHTML = angular.element('

{{total}}

'), + * scope = ....; + * + * var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) { + * //attach the clone to DOM document at the right place + * }); + * + * //now we have reference to the cloned DOM via `clone` + *
+ * + * + * For information on how the compiler works, see the + * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. + */ + + +/** + * @ngdoc service + * @name ng.$compileProvider + * @function + * + * @description + */ +$CompileProvider.$inject = ['$provide']; +function $CompileProvider($provide) { + var hasDirectives = {}, + Suffix = 'Directive', + COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, + CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, + MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ', + urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/; + + + /** + * @ngdoc function + * @name ng.$compileProvider#directive + * @methodOf ng.$compileProvider + * @function + * + * @description + * Register a new directives with the compiler. + * + * @param {string} name Name of the directive in camel-case. (ie ngBind which will match as + * ng-bind). + * @param {function} directiveFactory An injectable directive factory function. See {@link guide/directive} for more + * info. + * @returns {ng.$compileProvider} Self for chaining. + */ + this.directive = function registerDirective(name, directiveFactory) { + if (isString(name)) { + assertArg(directiveFactory, 'directive'); + if (!hasDirectives.hasOwnProperty(name)) { + hasDirectives[name] = []; + $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', + function($injector, $exceptionHandler) { + var directives = []; + forEach(hasDirectives[name], function(directiveFactory) { + try { + var directive = $injector.invoke(directiveFactory); + if (isFunction(directive)) { + directive = { compile: valueFn(directive) }; + } else if (!directive.compile && directive.link) { + directive.compile = valueFn(directive.link); + } + directive.priority = directive.priority || 0; + directive.name = directive.name || name; + directive.require = directive.require || (directive.controller && directive.name); + directive.restrict = directive.restrict || 'A'; + directives.push(directive); + } catch (e) { + $exceptionHandler(e); + } + }); + return directives; + }]); + } + hasDirectives[name].push(directiveFactory); + } else { + forEach(name, reverseParams(registerDirective)); + } + return this; + }; + + + /** + * @ngdoc function + * @name ng.$compileProvider#urlSanitizationWhitelist + * @methodOf ng.$compileProvider + * @function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an + * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular + * expression. If a match is found the original url is written into the dom. Otherwise the + * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.urlSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + urlSanitizationWhitelist = regexp; + return this; + } + return urlSanitizationWhitelist; + }; + + + this.$get = [ + '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', + '$controller', '$rootScope', '$document', + function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, + $controller, $rootScope, $document) { + + var Attributes = function(element, attr) { + this.$$element = element; + this.$attr = attr || {}; + }; + + Attributes.prototype = { + $normalize: directiveNormalize, + + + /** + * Set a normalized attribute on the element in a way such that all directives + * can share the attribute. This function properly handles boolean attributes. + * @param {string} key Normalized key. (ie ngAttribute) + * @param {string|boolean} value The value to set. If `null` attribute will be deleted. + * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. + * Defaults to true. + * @param {string=} attrName Optional none normalized name. Defaults to key. + */ + $set: function(key, value, writeAttr, attrName) { + var booleanKey = getBooleanAttrName(this.$$element[0], key), + $$observers = this.$$observers, + normalizedVal; + + if (booleanKey) { + this.$$element.prop(key, value); + attrName = booleanKey; + } + + this[key] = value; + + // translate normalized key to actual key + if (attrName) { + this.$attr[key] = attrName; + } else { + attrName = this.$attr[key]; + if (!attrName) { + this.$attr[key] = attrName = snake_case(key, '-'); + } + } + + + // sanitize a[href] values + if (nodeName_(this.$$element[0]) === 'A' && key === 'href') { + urlSanitizationNode.setAttribute('href', value); + + // href property always returns normalized absolute url, so we can match against that + normalizedVal = urlSanitizationNode.href; + if (!normalizedVal.match(urlSanitizationWhitelist)) { + this[key] = value = 'unsafe:' + normalizedVal; + } + } + + + if (writeAttr !== false) { + if (value === null || value === undefined) { + this.$$element.removeAttr(attrName); + } else { + this.$$element.attr(attrName, value); + } + } + + // fire observers + $$observers && forEach($$observers[key], function(fn) { + try { + fn(value); + } catch (e) { + $exceptionHandler(e); + } + }); + }, + + + /** + * Observe an interpolated attribute. + * The observer will never be called, if given attribute is not interpolated. + * + * @param {string} key Normalized key. (ie ngAttribute) . + * @param {function(*)} fn Function that will be called whenever the attribute value changes. + * @returns {function(*)} the `fn` Function passed in. + */ + $observe: function(key, fn) { + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = {})), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); + return fn; + } + }; + + var urlSanitizationNode = $document[0].createElement('a'), + startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') + ? identity + : function denormalizeTemplate(template) { + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + }, + NG_ATTR_BINDING = /^ngAttr[A-Z]/; + + + return compile; + + //================================ + + function compile($compileNodes, transcludeFn, maxPriority) { + if (!($compileNodes instanceof jqLite)) { + // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it. + $compileNodes = jqLite($compileNodes); + } + // We can not compile top level text elements since text nodes can be merged and we will + // not be able to attach scope data to them, so we will wrap them in + forEach($compileNodes, function(node, index){ + if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) { + $compileNodes[index] = jqLite(node).wrap('').parent()[0]; + } + }); + var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority); + return function publicLinkFn(scope, cloneConnectFn){ + assertArg(scope, 'scope'); + // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart + // and sometimes changes the structure of the DOM. + var $linkNode = cloneConnectFn + ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! + : $compileNodes; + + // Attach scope only to non-text nodes. + for(var i = 0, ii = $linkNode.length; i + addDirective(directives, + directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority); + + // iterate over the attributes + for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes, + j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { + attr = nAttrs[j]; + if (attr.specified) { + name = attr.name; + // support ngAttr attribute binding + ngAttrName = directiveNormalize(name); + if (NG_ATTR_BINDING.test(ngAttrName)) { + name = ngAttrName.substr(6).toLowerCase(); + } + nName = directiveNormalize(name.toLowerCase()); + attrsMap[nName] = name; + attrs[nName] = value = trim((msie && name == 'href') + ? decodeURIComponent(node.getAttribute(name, 2)) + : attr.value); + if (getBooleanAttrName(node, nName)) { + attrs[nName] = true; // presence means true + } + addAttrInterpolateDirective(node, directives, value, nName); + addDirective(directives, nName, 'A', maxPriority); + } + } + + // use class as directive + className = node.className; + if (isString(className) && className !== '') { + while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { + nName = directiveNormalize(match[2]); + if (addDirective(directives, nName, 'C', maxPriority)) { + attrs[nName] = trim(match[3]); + } + className = className.substr(match.index + match[0].length); + } + } + break; + case 3: /* Text Node */ + addTextInterpolateDirective(directives, node.nodeValue); + break; + case 8: /* Comment */ + try { + match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); + if (match) { + nName = directiveNormalize(match[1]); + if (addDirective(directives, nName, 'M', maxPriority)) { + attrs[nName] = trim(match[2]); + } + } + } catch (e) { + // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value. + // Just ignore it and continue. (Can't seem to reproduce in test case.) + } + break; + } + + directives.sort(byPriority); + return directives; + } + + + /** + * Once the directives have been collected, their compile functions are executed. This method + * is responsible for inlining directive templates as well as terminating the application + * of the directives if the terminal directive has been reached. + * + * @param {Array} directives Array of collected directives to execute their compile function. + * this needs to be pre-sorted by priority order. + * @param {Node} compileNode The raw DOM node to apply the compile functions to + * @param {Object} templateAttrs The shared attribute function + * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the + * scope argument is auto-generated to the new child of the transcluded parent scope. + * @param {JQLite} jqCollection If we are working on the root of the compile tree then this + * argument has the root jqLite array so that we can replace nodes on it. + * @returns linkFn + */ + function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) { + var terminalPriority = -Number.MAX_VALUE, + preLinkFns = [], + postLinkFns = [], + newScopeDirective = null, + newIsolateScopeDirective = null, + templateDirective = null, + $compileNode = templateAttrs.$$element = jqLite(compileNode), + directive, + directiveName, + $template, + transcludeDirective, + childTranscludeFn = transcludeFn, + controllerDirectives, + linkFn, + directiveValue; + + // executes all directives on the current element + for(var i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + $template = undefined; + + if (terminalPriority > directive.priority) { + break; // prevent further processing of directives + } + + if (directiveValue = directive.scope) { + assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode); + if (isObject(directiveValue)) { + safeAddClass($compileNode, 'ng-isolate-scope'); + newIsolateScopeDirective = directive; + } + safeAddClass($compileNode, 'ng-scope'); + newScopeDirective = newScopeDirective || directive; + } + + directiveName = directive.name; + + if (directiveValue = directive.controller) { + controllerDirectives = controllerDirectives || {}; + assertNoDuplicate("'" + directiveName + "' controller", + controllerDirectives[directiveName], directive, $compileNode); + controllerDirectives[directiveName] = directive; + } + + if (directiveValue = directive.transclude) { + assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); + transcludeDirective = directive; + terminalPriority = directive.priority; + if (directiveValue == 'element') { + $template = jqLite(compileNode); + $compileNode = templateAttrs.$$element = + jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); + compileNode = $compileNode[0]; + replaceWith(jqCollection, jqLite($template[0]), compileNode); + childTranscludeFn = compile($template, transcludeFn, terminalPriority); + } else { + $template = jqLite(JQLiteClone(compileNode)).contents(); + $compileNode.html(''); // clear contents + childTranscludeFn = compile($template, transcludeFn); + } + } + + if (directive.template) { + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + directiveValue = (isFunction(directive.template)) + ? directive.template($compileNode, templateAttrs) + : directive.template; + + directiveValue = denormalizeTemplate(directiveValue); + + if (directive.replace) { + $template = jqLite('
' + + trim(directiveValue) + + '
').contents(); + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue); + } + + replaceWith(jqCollection, $compileNode, compileNode); + + var newTemplateAttrs = {$attr: {}}; + + // combine directives from the original node and from the template: + // - take the array of directives for this element + // - split it into two parts, those that were already applied and those that weren't + // - collect directives from the template, add them to the second group and sort them + // - append the second group with new directives to the first group + directives = directives.concat( + collectDirectives( + compileNode, + directives.splice(i + 1, directives.length - (i + 1)), + newTemplateAttrs + ) + ); + mergeTemplateAttributes(templateAttrs, newTemplateAttrs); + + ii = directives.length; + } else { + $compileNode.html(directiveValue); + } + } + + if (directive.templateUrl) { + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), + nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace, + childTranscludeFn); + ii = directives.length; + } else if (directive.compile) { + try { + linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); + if (isFunction(linkFn)) { + addLinkFns(null, linkFn); + } else if (linkFn) { + addLinkFns(linkFn.pre, linkFn.post); + } + } catch (e) { + $exceptionHandler(e, startingTag($compileNode)); + } + } + + if (directive.terminal) { + nodeLinkFn.terminal = true; + terminalPriority = Math.max(terminalPriority, directive.priority); + } + + } + + nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope; + nodeLinkFn.transclude = transcludeDirective && childTranscludeFn; + + // might be normal or delayed nodeLinkFn depending on if templateUrl is present + return nodeLinkFn; + + //////////////////// + + function addLinkFns(pre, post) { + if (pre) { + pre.require = directive.require; + preLinkFns.push(pre); + } + if (post) { + post.require = directive.require; + postLinkFns.push(post); + } + } + + + function getControllers(require, $element) { + var value, retrievalMethod = 'data', optional = false; + if (isString(require)) { + while((value = require.charAt(0)) == '^' || value == '?') { + require = require.substr(1); + if (value == '^') { + retrievalMethod = 'inheritedData'; + } + optional = optional || value == '?'; + } + value = $element[retrievalMethod]('$' + require + 'Controller'); + if (!value && !optional) { + throw Error("No controller: " + require); + } + return value; + } else if (isArray(require)) { + value = []; + forEach(require, function(require) { + value.push(getControllers(require, $element)); + }); + } + return value; + } + + + function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { + var attrs, $element, i, ii, linkFn, controller; + + if (compileNode === linkNode) { + attrs = templateAttrs; + } else { + attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); + } + $element = attrs.$$element; + + if (newIsolateScopeDirective) { + var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; + + var parentScope = scope.$parent || scope; + + forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) { + var match = definiton.match(LOCAL_REGEXP) || [], + attrName = match[3] || scopeName, + optional = (match[2] == '?'), + mode = match[1], // @, =, or & + lastValue, + parentGet, parentSet; + + scope.$$isolateBindings[scopeName] = mode + attrName; + + switch (mode) { + + case '@': { + attrs.$observe(attrName, function(value) { + scope[scopeName] = value; + }); + attrs.$$observers[attrName].$$scope = parentScope; + if( attrs[attrName] ) { + // If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn + scope[scopeName] = $interpolate(attrs[attrName])(parentScope); + } + break; + } + + case '=': { + if (optional && !attrs[attrName]) { + return; + } + parentGet = $parse(attrs[attrName]); + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = scope[scopeName] = parentGet(parentScope); + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] + + ' (directive: ' + newIsolateScopeDirective.name + ')'); + }; + lastValue = scope[scopeName] = parentGet(parentScope); + scope.$watch(function parentValueWatch() { + var parentValue = parentGet(parentScope); + + if (parentValue !== scope[scopeName]) { + // we are out of sync and need to copy + if (parentValue !== lastValue) { + // parent changed and it has precedence + lastValue = scope[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(parentScope, parentValue = lastValue = scope[scopeName]); + } + } + return parentValue; + }); + break; + } + + case '&': { + parentGet = $parse(attrs[attrName]); + scope[scopeName] = function(locals) { + return parentGet(parentScope, locals); + }; + break; + } + + default: { + throw Error('Invalid isolate scope definition for directive ' + + newIsolateScopeDirective.name + ': ' + definiton); + } + } + }); + } + + if (controllerDirectives) { + forEach(controllerDirectives, function(directive) { + var locals = { + $scope: scope, + $element: $element, + $attrs: attrs, + $transclude: boundTranscludeFn + }; + + controller = directive.controller; + if (controller == '@') { + controller = attrs[directive.name]; + } + + $element.data( + '$' + directive.name + 'Controller', + $controller(controller, locals)); + }); + } + + // PRELINKING + for(i = 0, ii = preLinkFns.length; i < ii; i++) { + try { + linkFn = preLinkFns[i]; + linkFn(scope, $element, attrs, + linkFn.require && getControllers(linkFn.require, $element)); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + + // RECURSION + childLinkFn && childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn); + + // POSTLINKING + for(i = 0, ii = postLinkFns.length; i < ii; i++) { + try { + linkFn = postLinkFns[i]; + linkFn(scope, $element, attrs, + linkFn.require && getControllers(linkFn.require, $element)); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + } + } + + + /** + * looks up the directive and decorates it with exception handling and proper parameters. We + * call this the boundDirective. + * + * @param {string} name name of the directive to look up. + * @param {string} location The directive must be found in specific format. + * String containing any of theses characters: + * + * * `E`: element name + * * `A': attribute + * * `C`: class + * * `M`: comment + * @returns true if directive was added. + */ + function addDirective(tDirectives, name, location, maxPriority) { + var match = false; + if (hasDirectives.hasOwnProperty(name)) { + for(var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i directive.priority) && + directive.restrict.indexOf(location) != -1) { + tDirectives.push(directive); + match = true; + } + } catch(e) { $exceptionHandler(e); } + } + } + return match; + } + + + /** + * When the element is replaced with HTML template then the new attributes + * on the template need to be merged with the existing attributes in the DOM. + * The desired effect is to have both of the attributes present. + * + * @param {object} dst destination attributes (original DOM) + * @param {object} src source attributes (from the directive template) + */ + function mergeTemplateAttributes(dst, src) { + var srcAttr = src.$attr, + dstAttr = dst.$attr, + $element = dst.$$element; + + // reapply the old attributes to the new element + forEach(dst, function(value, key) { + if (key.charAt(0) != '$') { + if (src[key]) { + value += (key === 'style' ? ';' : ' ') + src[key]; + } + dst.$set(key, value, true, srcAttr[key]); + } + }); + + // copy the new attributes on the old attrs object + forEach(src, function(value, key) { + if (key == 'class') { + safeAddClass($element, value); + dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; + } else if (key == 'style') { + $element.attr('style', $element.attr('style') + ';' + value); + } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { + dst[key] = value; + dstAttr[key] = srcAttr[key]; + } + }); + } + + + function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs, + $rootElement, replace, childTranscludeFn) { + var linkQueue = [], + afterTemplateNodeLinkFn, + afterTemplateChildLinkFn, + beforeTemplateCompileNode = $compileNode[0], + origAsyncDirective = directives.shift(), + // The fact that we have to copy and patch the directive seems wrong! + derivedSyncDirective = extend({}, origAsyncDirective, { + controller: null, templateUrl: null, transclude: null, scope: null + }), + templateUrl = (isFunction(origAsyncDirective.templateUrl)) + ? origAsyncDirective.templateUrl($compileNode, tAttrs) + : origAsyncDirective.templateUrl; + + $compileNode.html(''); + + $http.get(templateUrl, {cache: $templateCache}). + success(function(content) { + var compileNode, tempTemplateAttrs, $template; + + content = denormalizeTemplate(content); + + if (replace) { + $template = jqLite('
' + trim(content) + '
').contents(); + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw new Error(MULTI_ROOT_TEMPLATE_ERROR + content); + } + + tempTemplateAttrs = {$attr: {}}; + replaceWith($rootElement, $compileNode, compileNode); + collectDirectives(compileNode, directives, tempTemplateAttrs); + mergeTemplateAttributes(tAttrs, tempTemplateAttrs); + } else { + compileNode = beforeTemplateCompileNode; + $compileNode.html(content); + } + + directives.unshift(derivedSyncDirective); + afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn); + afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); + + + while(linkQueue.length) { + var scope = linkQueue.shift(), + beforeTemplateLinkNode = linkQueue.shift(), + linkRootElement = linkQueue.shift(), + controller = linkQueue.shift(), + linkNode = compileNode; + + if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { + // it was cloned therefore we have to clone as well. + linkNode = JQLiteClone(compileNode); + replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); + } + + afterTemplateNodeLinkFn(function() { + beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller); + }, scope, linkNode, $rootElement, controller); + } + linkQueue = null; + }). + error(function(response, code, headers, config) { + throw Error('Failed to load template: ' + config.url); + }); + + return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) { + if (linkQueue) { + linkQueue.push(scope); + linkQueue.push(node); + linkQueue.push(rootElement); + linkQueue.push(controller); + } else { + afterTemplateNodeLinkFn(function() { + beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller); + }, scope, node, rootElement, controller); + } + }; + } + + + /** + * Sorting function for bound directives. + */ + function byPriority(a, b) { + return b.priority - a.priority; + } + + + function assertNoDuplicate(what, previousDirective, directive, element) { + if (previousDirective) { + throw Error('Multiple directives [' + previousDirective.name + ', ' + + directive.name + '] asking for ' + what + ' on: ' + startingTag(element)); + } + } + + + function addTextInterpolateDirective(directives, text) { + var interpolateFn = $interpolate(text, true); + if (interpolateFn) { + directives.push({ + priority: 0, + compile: valueFn(function textInterpolateLinkFn(scope, node) { + var parent = node.parent(), + bindings = parent.data('$binding') || []; + bindings.push(interpolateFn); + safeAddClass(parent.data('$binding', bindings), 'ng-binding'); + scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { + node[0].nodeValue = value; + }); + }) + }); + } + } + + + function addAttrInterpolateDirective(node, directives, value, name) { + var interpolateFn = $interpolate(value, true); + + // no interpolation found -> ignore + if (!interpolateFn) return; + + + directives.push({ + priority: 100, + compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = {})); + + // we need to interpolate again, in case the attribute value has been updated + // (e.g. by another directive's compile function) + interpolateFn = $interpolate(attr[name], true); + + // if attribute was updated so that there is no interpolation going on we don't want to + // register any observers + if (!interpolateFn) return; + + attr[name] = interpolateFn(scope); + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function interpolateFnWatchAction(value) { + attr.$set(name, value); + }); + }) + }); + } + + + /** + * This is a special jqLite.replaceWith, which can replace items which + * have no parents, provided that the containing jqLite collection is provided. + * + * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes + * in the root of the tree. + * @param {JqLite} $element The jqLite element which we are going to replace. We keep the shell, + * but replace its DOM node reference. + * @param {Node} newNode The new DOM node. + */ + function replaceWith($rootElement, $element, newNode) { + var oldNode = $element[0], + parent = oldNode.parentNode, + i, ii; + + if ($rootElement) { + for(i = 0, ii = $rootElement.length; i < ii; i++) { + if ($rootElement[i] == oldNode) { + $rootElement[i] = newNode; + break; + } + } + } + + if (parent) { + parent.replaceChild(newNode, oldNode); + } + + newNode[jqLite.expando] = oldNode[jqLite.expando]; + $element[0] = newNode; + } + }]; +} + +var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; +/** + * Converts all accepted directives format into proper directive name. + * All of these will become 'myDirective': + * my:DiRective + * my-directive + * x-my-directive + * data-my:directive + * + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function directiveNormalize(name) { + return camelCase(name.replace(PREFIX_REGEXP, '')); +} + +/** + * @ngdoc object + * @name ng.$compile.directive.Attributes + * @description + * + * A shared object between directive compile / linking functions which contains normalized DOM element + * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed + * since all of these are treated as equivalent in Angular: + * + * + */ + +/** + * @ngdoc property + * @name ng.$compile.directive.Attributes#$attr + * @propertyOf ng.$compile.directive.Attributes + * @returns {object} A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc function + * @name ng.$compile.directive.Attributes#$set + * @methodOf ng.$compile.directive.Attributes + * @function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. The value can be an interpolated string. + */ + + + +/** + * Closure compiler type information + */ + +function nodesetLinkingFn( + /* angular.Scope */ scope, + /* NodeList */ nodeList, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +){} + +function directiveLinkingFn( + /* nodesetLinkingFn */ nodesetLinkingFn, + /* angular.Scope */ scope, + /* Node */ node, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +){} + +/** + * @ngdoc object + * @name ng.$controllerProvider + * @description + * The {@link ng.$controller $controller service} is used by Angular to create new + * controllers. + * + * This provider allows controller registration via the + * {@link ng.$controllerProvider#register register} method. + */ +function $ControllerProvider() { + var controllers = {}, + CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; + + + /** + * @ngdoc function + * @name ng.$controllerProvider#register + * @methodOf ng.$controllerProvider + * @param {string} name Controller name + * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI + * annotations in the array notation). + */ + this.register = function(name, constructor) { + if (isObject(name)) { + extend(controllers, name) + } else { + controllers[name] = constructor; + } + }; + + + this.$get = ['$injector', '$window', function($injector, $window) { + + /** + * @ngdoc function + * @name ng.$controller + * @requires $injector + * + * @param {Function|string} constructor If called with a function then it's considered to be the + * controller constructor function. Otherwise it's considered to be a string which is used + * to retrieve the controller constructor using the following steps: + * + * * check if a controller with given name is registered via `$controllerProvider` + * * check if evaluating the string on the current scope returns a constructor + * * check `window[constructor]` on the global `window` object + * + * @param {Object} locals Injection locals for Controller. + * @return {Object} Instance of given controller. + * + * @description + * `$controller` service is responsible for instantiating controllers. + * + * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into + * a service, so that one can override this service with {@link https://gist.github.com/1649788 + * BC version}. + */ + return function(expression, locals) { + var instance, match, constructor, identifier; + + if(isString(expression)) { + match = expression.match(CNTRL_REG), + constructor = match[1], + identifier = match[3]; + expression = controllers.hasOwnProperty(constructor) + ? controllers[constructor] + : getter(locals.$scope, constructor, true) || getter($window, constructor, true); + + assertArgFn(expression, constructor, true); + } + + instance = $injector.instantiate(expression, locals); + + if (identifier) { + if (typeof locals.$scope !== 'object') { + throw new Error('Can not export controller as "' + identifier + '". ' + + 'No scope object provided!'); + } + + locals.$scope[identifier] = instance; + } + + return instance; + }; + }]; +} + +/** + * @ngdoc object + * @name ng.$document + * @requires $window + * + * @description + * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document` + * element. + */ +function $DocumentProvider(){ + this.$get = ['$window', function(window){ + return jqLite(window.document); + }]; +} + +/** + * @ngdoc function + * @name ng.$exceptionHandler + * @requires $log + * + * @description + * Any uncaught exception in angular expressions is delegated to this service. + * The default implementation simply delegates to `$log.error` which logs it into + * the browser console. + * + * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by + * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. + * + * @param {Error} exception Exception associated with the error. + * @param {string=} cause optional information about the context in which + * the error was thrown. + * + */ +function $ExceptionHandlerProvider() { + this.$get = ['$log', function($log) { + return function(exception, cause) { + $log.error.apply($log, arguments); + }; + }]; +} + +/** + * @ngdoc object + * @name ng.$interpolateProvider + * @function + * + * @description + * + * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. + */ +function $InterpolateProvider() { + var startSymbol = '{{'; + var endSymbol = '}}'; + + /** + * @ngdoc method + * @name ng.$interpolateProvider#startSymbol + * @methodOf ng.$interpolateProvider + * @description + * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. + * + * @param {string=} value new value to set the starting symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.startSymbol = function(value){ + if (value) { + startSymbol = value; + return this; + } else { + return startSymbol; + } + }; + + /** + * @ngdoc method + * @name ng.$interpolateProvider#endSymbol + * @methodOf ng.$interpolateProvider + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * @param {string=} value new value to set the ending symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.endSymbol = function(value){ + if (value) { + endSymbol = value; + return this; + } else { + return endSymbol; + } + }; + + + this.$get = ['$parse', '$exceptionHandler', function($parse, $exceptionHandler) { + var startSymbolLength = startSymbol.length, + endSymbolLength = endSymbol.length; + + /** + * @ngdoc function + * @name ng.$interpolate + * @function + * + * @requires $parse + * + * @description + * + * Compiles a string with markup into an interpolation function. This service is used by the + * HTML {@link ng.$compile $compile} service for data binding. See + * {@link ng.$interpolateProvider $interpolateProvider} for configuring the + * interpolation markup. + * + * +
+         var $interpolate = ...; // injected
+         var exp = $interpolate('Hello {{name}}!');
+         expect(exp({name:'Angular'}).toEqual('Hello Angular!');
+       
+ * + * + * @param {string} text The text with markup to interpolate. + * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have + * embedded expression in order to return an interpolation function. Strings with no + * embedded expression will return null for the interpolation function. + * @returns {function(context)} an interpolation function which is used to compute the interpolated + * string. The function has these parameters: + * + * * `context`: an object against which any expressions embedded in the strings are evaluated + * against. + * + */ + function $interpolate(text, mustHaveExpression) { + var startIndex, + endIndex, + index = 0, + parts = [], + length = text.length, + hasInterpolation = false, + fn, + exp, + concat = []; + + while(index < length) { + if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && + ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { + (index != startIndex) && parts.push(text.substring(index, startIndex)); + parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex))); + fn.exp = exp; + index = endIndex + endSymbolLength; + hasInterpolation = true; + } else { + // we did not find anything, so we have to add the remainder to the parts array + (index != length) && parts.push(text.substring(index)); + index = length; + } + } + + if (!(length = parts.length)) { + // we added, nothing, must have been an empty string. + parts.push(''); + length = 1; + } + + if (!mustHaveExpression || hasInterpolation) { + concat.length = length; + fn = function(context) { + try { + for(var i = 0, ii = length, part; i=} search New search params - string or hash object + * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a + * single search parameter. If the value is `null`, the parameter will be deleted. + * + * @return {string} search + */ + search: function(search, paramValue) { + if (isUndefined(search)) + return this.$$search; + + if (isDefined(paramValue)) { + if (paramValue === null) { + delete this.$$search[search]; + } else { + this.$$search[search] = paramValue; + } + } else { + this.$$search = isString(search) ? parseKeyValue(search) : search; + } + + this.$$compose(); + return this; + }, + + /** + * @ngdoc method + * @name ng.$location#hash + * @methodOf ng.$location + * + * @description + * This method is getter / setter. + * + * Return hash fragment when called without any parameter. + * + * Change hash fragment when called with parameter and return `$location`. + * + * @param {string=} hash New hash fragment + * @return {string} hash + */ + hash: locationGetterSetter('$$hash', identity), + + /** + * @ngdoc method + * @name ng.$location#replace + * @methodOf ng.$location + * + * @description + * If called, all changes to $location during current `$digest` will be replacing current history + * record, instead of adding new one. + */ + replace: function() { + this.$$replace = true; + return this; + } +}; + +function locationGetter(property) { + return function() { + return this[property]; + }; +} + + +function locationGetterSetter(property, preprocess) { + return function(value) { + if (isUndefined(value)) + return this[property]; + + this[property] = preprocess(value); + this.$$compose(); + + return this; + }; +} + + +/** + * @ngdoc object + * @name ng.$location + * + * @requires $browser + * @requires $sniffer + * @requires $rootElement + * + * @description + * The $location service parses the URL in the browser address bar (based on the + * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. + * + * **The $location service:** + * + * - Exposes the current URL in the browser address bar, so you can + * - Watch and observe the URL. + * - Change the URL. + * - Synchronizes the URL with the browser when the user + * - Changes the address bar. + * - Clicks the back or forward button (or clicks a History link). + * - Clicks on a link. + * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). + * + * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular + * Services: Using $location} + */ + +/** + * @ngdoc object + * @name ng.$locationProvider + * @description + * Use the `$locationProvider` to configure how the application deep linking paths are stored. + */ +function $LocationProvider(){ + var hashPrefix = '', + html5Mode = false; + + /** + * @ngdoc property + * @name ng.$locationProvider#hashPrefix + * @methodOf ng.$locationProvider + * @description + * @param {string=} prefix Prefix for hash part (containing path and search) + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.hashPrefix = function(prefix) { + if (isDefined(prefix)) { + hashPrefix = prefix; + return this; + } else { + return hashPrefix; + } + }; + + /** + * @ngdoc property + * @name ng.$locationProvider#html5Mode + * @methodOf ng.$locationProvider + * @description + * @param {string=} mode Use HTML5 strategy if available. + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.html5Mode = function(mode) { + if (isDefined(mode)) { + html5Mode = mode; + return this; + } else { + return html5Mode; + } + }; + + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', + function( $rootScope, $browser, $sniffer, $rootElement) { + var $location, + LocationMode, + baseHref = $browser.baseHref(), + initialUrl = $browser.url(), + appBase; + + if (html5Mode) { + appBase = baseHref ? serverBase(initialUrl) + baseHref : initialUrl; + LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; + } else { + appBase = stripHash(initialUrl); + LocationMode = LocationHashbangUrl; + } + $location = new LocationMode(appBase, '#' + hashPrefix); + $location.$$parse($location.$$rewrite(initialUrl)); + + $rootElement.bind('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (event.ctrlKey || event.metaKey || event.which == 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (lowercase(elm[0].nodeName) !== 'a') { + // ignore rewriting if no A tag (reached root element, or no parent - removed from document) + if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; + } + + var absHref = elm.prop('href'); + var rewrittenUrl = $location.$$rewrite(absHref); + + if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) { + event.preventDefault(); + if (rewrittenUrl != $browser.url()) { + // update location manually + $location.$$parse(rewrittenUrl); + $rootScope.$apply(); + // hack to work around FF6 bug 684208 when scenario runner clicks on links + window.angular['ff-684208-preventDefault'] = true; + } + } + }); + + + // rewrite hashbang url <> html5 url + if ($location.absUrl() != initialUrl) { + $browser.url($location.absUrl(), true); + } + + // update $location when $browser url changes + $browser.onUrlChange(function(newUrl) { + if ($location.absUrl() != newUrl) { + if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) { + $browser.url($location.absUrl()); + return; + } + $rootScope.$evalAsync(function() { + var oldUrl = $location.absUrl(); + + $location.$$parse(newUrl); + afterLocationChange(oldUrl); + }); + if (!$rootScope.$$phase) $rootScope.$digest(); + } + }); + + // update browser + var changeCounter = 0; + $rootScope.$watch(function $locationWatch() { + var oldUrl = $browser.url(); + var currentReplace = $location.$$replace; + + if (!changeCounter || oldUrl != $location.absUrl()) { + changeCounter++; + $rootScope.$evalAsync(function() { + if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). + defaultPrevented) { + $location.$$parse(oldUrl); + } else { + $browser.url($location.absUrl(), currentReplace); + afterLocationChange(oldUrl); + } + }); + } + $location.$$replace = false; + + return changeCounter; + }); + + return $location; + + function afterLocationChange(oldUrl) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); + } +}]; +} + +/** + * @ngdoc object + * @name ng.$log + * @requires $window + * + * @description + * Simple service for logging. Default implementation writes the message + * into the browser's console (if present). + * + * The main purpose of this service is to simplify debugging and troubleshooting. + * + * @example + + + function LogCtrl($scope, $log) { + $scope.$log = $log; + $scope.message = 'Hello World!'; + } + + +
+

Reload this page with open console, enter text and hit the log button...

+ Message: + + + + + +
+
+
+ */ + +/** + * @ngdoc object + * @name ng.$logProvider + * @description + * Use the `$logProvider` to configure how the application logs messages + */ +function $LogProvider(){ + var debug = true, + self = this; + + /** + * @ngdoc property + * @name ng.$logProvider#debugEnabled + * @methodOf ng.$logProvider + * @description + * @param {string=} flag enable or disable debug level messages + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.debugEnabled = function(flag) { + if (isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } + }; + + this.$get = ['$window', function($window){ + return { + /** + * @ngdoc method + * @name ng.$log#log + * @methodOf ng.$log + * + * @description + * Write a log message + */ + log: consoleLog('log'), + + /** + * @ngdoc method + * @name ng.$log#warn + * @methodOf ng.$log + * + * @description + * Write a warning message + */ + warn: consoleLog('warn'), + + /** + * @ngdoc method + * @name ng.$log#info + * @methodOf ng.$log + * + * @description + * Write an information message + */ + info: consoleLog('info'), + + /** + * @ngdoc method + * @name ng.$log#error + * @methodOf ng.$log + * + * @description + * Write an error message + */ + error: consoleLog('error'), + + /** + * @ngdoc method + * @name ng.$log#debug + * @methodOf ng.$log + * + * @description + * Write a debug message + */ + debug: (function () { + var fn = consoleLog('debug'); + + return function() { + if (debug) { + fn.apply(self, arguments); + } + } + }()) + }; + + function formatError(arg) { + if (arg instanceof Error) { + if (arg.stack) { + arg = (arg.message && arg.stack.indexOf(arg.message) === -1) + ? 'Error: ' + arg.message + '\n' + arg.stack + : arg.stack; + } else if (arg.sourceURL) { + arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; + } + } + return arg; + } + + function consoleLog(type) { + var console = $window.console || {}, + logFn = console[type] || console.log || noop; + + if (logFn.apply) { + return function() { + var args = []; + forEach(arguments, function(arg) { + args.push(formatError(arg)); + }); + return logFn.apply(console, args); + }; + } + + // we are IE which either doesn't have window.console => this is noop and we do nothing, + // or we are IE where console.log doesn't have apply so we log at least first 2 args + return function(arg1, arg2) { + logFn(arg1, arg2); + } + } + }]; +} + +var OPERATORS = { + 'null':function(){return null;}, + 'true':function(){return true;}, + 'false':function(){return false;}, + undefined:noop, + '+':function(self, locals, a,b){ + a=a(self, locals); b=b(self, locals); + if (isDefined(a)) { + if (isDefined(b)) { + return a + b; + } + return a; + } + return isDefined(b)?b:undefined;}, + '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, + '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, + '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, + '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, + '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, + '=':noop, + '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);}, + '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);}, + '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, + '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, + '<':function(self, locals, a,b){return a(self, locals)':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, + '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, + '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, + '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, + '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, + '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, +// '|':function(self, locals, a,b){return a|b;}, + '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, + '!':function(self, locals, a){return !a(self, locals);} +}; +var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; + +function lex(text, csp){ + var tokens = [], + token, + index = 0, + json = [], + ch, + lastCh = ':'; // can start regexp + + while (index < text.length) { + ch = text.charAt(index); + if (is('"\'')) { + readString(ch); + } else if (isNumber(ch) || is('.') && isNumber(peek())) { + readNumber(); + } else if (isIdent(ch)) { + readIdent(); + // identifiers can only be if the preceding char was a { or , + if (was('{,') && json[0]=='{' && + (token=tokens[tokens.length-1])) { + token.json = token.text.indexOf('.') == -1; + } + } else if (is('(){}[].,;:?')) { + tokens.push({ + index:index, + text:ch, + json:(was(':[,') && is('{[')) || is('}]:,') + }); + if (is('{[')) json.unshift(ch); + if (is('}]')) json.shift(); + index++; + } else if (isWhitespace(ch)) { + index++; + continue; + } else { + var ch2 = ch + peek(), + ch3 = ch2 + peek(2), + fn = OPERATORS[ch], + fn2 = OPERATORS[ch2], + fn3 = OPERATORS[ch3]; + if (fn3) { + tokens.push({index:index, text:ch3, fn:fn3}); + index += 3; + } else if (fn2) { + tokens.push({index:index, text:ch2, fn:fn2}); + index += 2; + } else if (fn) { + tokens.push({index:index, text:ch, fn:fn, json: was('[,:') && is('+-')}); + index += 1; + } else { + throwError("Unexpected next character ", index, index+1); + } + } + lastCh = ch; + } + return tokens; + + function is(chars) { + return chars.indexOf(ch) != -1; + } + + function was(chars) { + return chars.indexOf(lastCh) != -1; + } + + function peek(i) { + var num = i || 1; + return index + num < text.length ? text.charAt(index + num) : false; + } + function isNumber(ch) { + return '0' <= ch && ch <= '9'; + } + function isWhitespace(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || + ch == '\n' || ch == '\v' || ch == '\u00A0'; // IE treats non-breaking space as \u00A0 + } + function isIdent(ch) { + return 'a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' == ch || ch == '$'; + } + function isExpOperator(ch) { + return ch == '-' || ch == '+' || isNumber(ch); + } + + function throwError(error, start, end) { + end = end || index; + throw Error("Lexer Error: " + error + " at column" + + (isDefined(start) + ? "s " + start + "-" + index + " [" + text.substring(start, end) + "]" + : " " + end) + + " in expression [" + text + "]."); + } + + function readNumber() { + var number = ""; + var start = index; + while (index < text.length) { + var ch = lowercase(text.charAt(index)); + if (ch == '.' || isNumber(ch)) { + number += ch; + } else { + var peekCh = peek(); + if (ch == 'e' && isExpOperator(peekCh)) { + number += ch; + } else if (isExpOperator(ch) && + peekCh && isNumber(peekCh) && + number.charAt(number.length - 1) == 'e') { + number += ch; + } else if (isExpOperator(ch) && + (!peekCh || !isNumber(peekCh)) && + number.charAt(number.length - 1) == 'e') { + throwError('Invalid exponent'); + } else { + break; + } + } + index++; + } + number = 1 * number; + tokens.push({index:start, text:number, json:true, + fn:function() {return number;}}); + } + function readIdent() { + var ident = "", + start = index, + lastDot, peekIndex, methodName, ch; + + while (index < text.length) { + ch = text.charAt(index); + if (ch == '.' || isIdent(ch) || isNumber(ch)) { + if (ch == '.') lastDot = index; + ident += ch; + } else { + break; + } + index++; + } + + //check if this is not a method invocation and if it is back out to last dot + if (lastDot) { + peekIndex = index; + while(peekIndex < text.length) { + ch = text.charAt(peekIndex); + if (ch == '(') { + methodName = ident.substr(lastDot - start + 1); + ident = ident.substr(0, lastDot - start); + index = peekIndex; + break; + } + if(isWhitespace(ch)) { + peekIndex++; + } else { + break; + } + } + } + + + var token = { + index:start, + text:ident + }; + + if (OPERATORS.hasOwnProperty(ident)) { + token.fn = token.json = OPERATORS[ident]; + } else { + var getter = getterFn(ident, csp); + token.fn = extend(function(self, locals) { + return (getter(self, locals)); + }, { + assign: function(self, value) { + return setter(self, ident, value); + } + }); + } + + tokens.push(token); + + if (methodName) { + tokens.push({ + index:lastDot, + text: '.', + json: false + }); + tokens.push({ + index: lastDot + 1, + text: methodName, + json: false + }); + } + } + + function readString(quote) { + var start = index; + index++; + var string = ""; + var rawString = quote; + var escape = false; + while (index < text.length) { + var ch = text.charAt(index); + rawString += ch; + if (escape) { + if (ch == 'u') { + var hex = text.substring(index + 1, index + 5); + if (!hex.match(/[\da-f]{4}/i)) + throwError( "Invalid unicode escape [\\u" + hex + "]"); + index += 4; + string += String.fromCharCode(parseInt(hex, 16)); + } else { + var rep = ESCAPE[ch]; + if (rep) { + string += rep; + } else { + string += ch; + } + } + escape = false; + } else if (ch == '\\') { + escape = true; + } else if (ch == quote) { + index++; + tokens.push({ + index:start, + text:rawString, + string:string, + json:true, + fn:function() { return string; } + }); + return; + } else { + string += ch; + } + index++; + } + throwError("Unterminated quote", start); + } +} + +///////////////////////////////////////// + +function parser(text, json, $filter, csp){ + var ZERO = valueFn(0), + value, + tokens = lex(text, csp), + assignment = _assignment, + functionCall = _functionCall, + fieldAccess = _fieldAccess, + objectIndex = _objectIndex, + filterChain = _filterChain; + + if(json){ + // The extra level of aliasing is here, just in case the lexer misses something, so that + // we prevent any accidental execution in JSON. + assignment = logicalOR; + functionCall = + fieldAccess = + objectIndex = + filterChain = + function() { throwError("is not valid json", {text:text, index:0}); }; + value = primary(); + } else { + value = statements(); + } + if (tokens.length !== 0) { + throwError("is an unexpected token", tokens[0]); + } + value.literal = !!value.literal; + value.constant = !!value.constant; + return value; + + /////////////////////////////////// + function throwError(msg, token) { + throw Error("Syntax Error: Token '" + token.text + + "' " + msg + " at column " + + (token.index + 1) + " of the expression [" + + text + "] starting at [" + text.substring(token.index) + "]."); + } + + function peekToken() { + if (tokens.length === 0) + throw Error("Unexpected end of expression: " + text); + return tokens[0]; + } + + function peek(e1, e2, e3, e4) { + if (tokens.length > 0) { + var token = tokens[0]; + var t = token.text; + if (t==e1 || t==e2 || t==e3 || t==e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + } + + function expect(e1, e2, e3, e4){ + var token = peek(e1, e2, e3, e4); + if (token) { + if (json && !token.json) { + throwError("is not valid json", token); + } + tokens.shift(); + return token; + } + return false; + } + + function consume(e1){ + if (!expect(e1)) { + throwError("is unexpected, expecting [" + e1 + "]", peek()); + } + } + + function unaryFn(fn, right) { + return extend(function(self, locals) { + return fn(self, locals, right); + }, { + constant:right.constant + }); + } + + function ternaryFn(left, middle, right){ + return extend(function(self, locals){ + return left(self, locals) ? middle(self, locals) : right(self, locals); + }, { + constant: left.constant && middle.constant && right.constant + }); + } + + function binaryFn(left, fn, right) { + return extend(function(self, locals) { + return fn(self, locals, left, right); + }, { + constant:left.constant && right.constant + }); + } + + function statements() { + var statements = []; + while(true) { + if (tokens.length > 0 && !peek('}', ')', ';', ']')) + statements.push(filterChain()); + if (!expect(';')) { + // optimize for the common case where there is only one statement. + // TODO(size): maybe we should not support multiple statements? + return statements.length == 1 + ? statements[0] + : function(self, locals){ + var value; + for ( var i = 0; i < statements.length; i++) { + var statement = statements[i]; + if (statement) + value = statement(self, locals); + } + return value; + }; + } + } + } + + function _filterChain() { + var left = expression(); + var token; + while(true) { + if ((token = expect('|'))) { + left = binaryFn(left, token.fn, filter()); + } else { + return left; + } + } + } + + function filter() { + var token = expect(); + var fn = $filter(token.text); + var argsFn = []; + while(true) { + if ((token = expect(':'))) { + argsFn.push(expression()); + } else { + var fnInvoke = function(self, locals, input){ + var args = [input]; + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](self, locals)); + } + return fn.apply(self, args); + }; + return function() { + return fnInvoke; + }; + } + } + } + + function expression() { + return assignment(); + } + + function _assignment() { + var left = ternary(); + var right; + var token; + if ((token = expect('='))) { + if (!left.assign) { + throwError("implies assignment but [" + + text.substring(0, token.index) + "] can not be assigned to", token); + } + right = ternary(); + return function(scope, locals){ + return left.assign(scope, right(scope, locals), locals); + }; + } else { + return left; + } + } + + function ternary() { + var left = logicalOR(); + var middle; + var token; + if((token = expect('?'))){ + middle = ternary(); + if((token = expect(':'))){ + return ternaryFn(left, middle, ternary()); + } + else { + throwError('expected :', token); + } + } + else { + return left; + } + } + + function logicalOR() { + var left = logicalAND(); + var token; + while(true) { + if ((token = expect('||'))) { + left = binaryFn(left, token.fn, logicalAND()); + } else { + return left; + } + } + } + + function logicalAND() { + var left = equality(); + var token; + if ((token = expect('&&'))) { + left = binaryFn(left, token.fn, logicalAND()); + } + return left; + } + + function equality() { + var left = relational(); + var token; + if ((token = expect('==','!=','===','!=='))) { + left = binaryFn(left, token.fn, equality()); + } + return left; + } + + function relational() { + var left = additive(); + var token; + if ((token = expect('<', '>', '<=', '>='))) { + left = binaryFn(left, token.fn, relational()); + } + return left; + } + + function additive() { + var left = multiplicative(); + var token; + while ((token = expect('+','-'))) { + left = binaryFn(left, token.fn, multiplicative()); + } + return left; + } + + function multiplicative() { + var left = unary(); + var token; + while ((token = expect('*','/','%'))) { + left = binaryFn(left, token.fn, unary()); + } + return left; + } + + function unary() { + var token; + if (expect('+')) { + return primary(); + } else if ((token = expect('-'))) { + return binaryFn(ZERO, token.fn, unary()); + } else if ((token = expect('!'))) { + return unaryFn(token.fn, unary()); + } else { + return primary(); + } + } + + + function primary() { + var primary; + if (expect('(')) { + primary = filterChain(); + consume(')'); + } else if (expect('[')) { + primary = arrayDeclaration(); + } else if (expect('{')) { + primary = object(); + } else { + var token = expect(); + primary = token.fn; + if (!primary) { + throwError("not a primary expression", token); + } + if (token.json) { + primary.constant = primary.literal = true; + } + } + + var next, context; + while ((next = expect('(', '[', '.'))) { + if (next.text === '(') { + primary = functionCall(primary, context); + context = null; + } else if (next.text === '[') { + context = primary; + primary = objectIndex(primary); + } else if (next.text === '.') { + context = primary; + primary = fieldAccess(primary); + } else { + throwError("IMPOSSIBLE"); + } + } + return primary; + } + + function _fieldAccess(object) { + var field = expect().text; + var getter = getterFn(field, csp); + return extend( + function(scope, locals, self) { + return getter(self || object(scope, locals), locals); + }, + { + assign:function(scope, value, locals) { + return setter(object(scope, locals), field, value); + } + } + ); + } + + function _objectIndex(obj) { + var indexFn = expression(); + consume(']'); + return extend( + function(self, locals){ + var o = obj(self, locals), + i = indexFn(self, locals), + v, p; + + if (!o) return undefined; + v = o[i]; + if (v && v.then) { + p = v; + if (!('$$v' in v)) { + p.$$v = undefined; + p.then(function(val) { p.$$v = val; }); + } + v = v.$$v; + } + return v; + }, { + assign:function(self, value, locals){ + return obj(self, locals)[indexFn(self, locals)] = value; + } + }); + } + + function _functionCall(fn, contextGetter) { + var argsFn = []; + if (peekToken().text != ')') { + do { + argsFn.push(expression()); + } while (expect(',')); + } + consume(')'); + return function(scope, locals){ + var args = [], + context = contextGetter ? contextGetter(scope, locals) : scope; + + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](scope, locals)); + } + var fnPtr = fn(scope, locals, context) || noop; + // IE stupidity! + return fnPtr.apply + ? fnPtr.apply(context, args) + : fnPtr(args[0], args[1], args[2], args[3], args[4]); + }; + } + + // This is used with json array declaration + function arrayDeclaration () { + var elementFns = []; + var allConstant = true; + if (peekToken().text != ']') { + do { + var elementFn = expression(); + elementFns.push(elementFn); + if (!elementFn.constant) { + allConstant = false; + } + } while (expect(',')); + } + consume(']'); + return extend(function(self, locals){ + var array = []; + for ( var i = 0; i < elementFns.length; i++) { + array.push(elementFns[i](self, locals)); + } + return array; + }, { + literal:true, + constant:allConstant + }); + } + + function object () { + var keyValues = []; + var allConstant = true; + if (peekToken().text != '}') { + do { + var token = expect(), + key = token.string || token.text; + consume(":"); + var value = expression(); + keyValues.push({key:key, value:value}); + if (!value.constant) { + allConstant = false; + } + } while (expect(',')); + } + consume('}'); + return extend(function(self, locals){ + var object = {}; + for ( var i = 0; i < keyValues.length; i++) { + var keyValue = keyValues[i]; + object[keyValue.key] = keyValue.value(self, locals); + } + return object; + }, { + literal:true, + constant:allConstant + }); + } +} + +////////////////////////////////////////////////// +// Parser helper functions +////////////////////////////////////////////////// + +function setter(obj, path, setValue) { + var element = path.split('.'); + for (var i = 0; element.length > 1; i++) { + var key = element.shift(); + var propertyObj = obj[key]; + if (!propertyObj) { + propertyObj = {}; + obj[key] = propertyObj; + } + obj = propertyObj; + } + obj[element.shift()] = setValue; + return setValue; +} + +/** + * Return the value accessible from the object by path. Any undefined traversals are ignored + * @param {Object} obj starting object + * @param {string} path path to traverse + * @param {boolean=true} bindFnToScope + * @returns value as accessible by path + */ +//TODO(misko): this function needs to be removed +function getter(obj, path, bindFnToScope) { + if (!path) return obj; + var keys = path.split('.'); + var key; + var lastInstance = obj; + var len = keys.length; + + for (var i = 0; i < len; i++) { + key = keys[i]; + if (obj) { + obj = (lastInstance = obj)[key]; + } + } + if (!bindFnToScope && isFunction(obj)) { + return bind(lastInstance, obj); + } + return obj; +} + +var getterFnCache = {}; + +/** + * Implementation of the "Black Hole" variant from: + * - http://jsperf.com/angularjs-parse-getter/4 + * - http://jsperf.com/path-evaluation-simplified/7 + */ +function cspSafeGetterFn(key0, key1, key2, key3, key4) { + return function(scope, locals) { + var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, + promise; + + if (pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key0]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key1 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key1]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key2 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key2]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key3 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key3]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key4 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key4]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + return pathVal; + }; +} + +function getterFn(path, csp) { + if (getterFnCache.hasOwnProperty(path)) { + return getterFnCache[path]; + } + + var pathKeys = path.split('.'), + pathKeysLength = pathKeys.length, + fn; + + if (csp) { + fn = (pathKeysLength < 6) + ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4]) + : function(scope, locals) { + var i = 0, val; + do { + val = cspSafeGetterFn( + pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++] + )(scope, locals); + + locals = undefined; // clear after first iteration + scope = val; + } while (i < pathKeysLength); + return val; + } + } else { + var code = 'var l, fn, p;\n'; + forEach(pathKeys, function(key, index) { + code += 'if(s === null || s === undefined) return s;\n' + + 'l=s;\n' + + 's='+ (index + // we simply dereference 's' on any .dot notation + ? 's' + // but if we are first then we check locals first, and if so read it first + : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + + 'if (s && s.then) {\n' + + ' if (!("$$v" in s)) {\n' + + ' p=s;\n' + + ' p.$$v = undefined;\n' + + ' p.then(function(v) {p.$$v=v;});\n' + + '}\n' + + ' s=s.$$v\n' + + '}\n'; + }); + code += 'return s;'; + fn = Function('s', 'k', code); // s=scope, k=locals + fn.toString = function() { return code; }; + } + + return getterFnCache[path] = fn; +} + +/////////////////////////////////// + +/** + * @ngdoc function + * @name ng.$parse + * @function + * + * @description + * + * Converts Angular {@link guide/expression expression} into a function. + * + *
+ *   var getter = $parse('user.name');
+ *   var setter = getter.assign;
+ *   var context = {user:{name:'angular'}};
+ *   var locals = {user:{name:'local'}};
+ *
+ *   expect(getter(context)).toEqual('angular');
+ *   setter(context, 'newValue');
+ *   expect(context.user.name).toEqual('newValue');
+ *   expect(getter(context, locals)).toEqual('local');
+ * 
+ * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + * + * The returned function also has the following properties: + * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript + * literal. + * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript + * constant literals. + * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be + * set to a function to change its value on the given context. + * + */ +function $ParseProvider() { + var cache = {}; + this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { + return function(exp) { + switch(typeof exp) { + case 'string': + return cache.hasOwnProperty(exp) + ? cache[exp] + : cache[exp] = parser(exp, false, $filter, $sniffer.csp); + case 'function': + return exp; + default: + return noop; + } + }; + }]; +} + +/** + * @ngdoc service + * @name ng.$q + * @requires $rootScope + * + * @description + * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). + * + * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an + * interface for interacting with an object that represents the result of an action that is + * performed asynchronously, and may or may not be finished at any given point in time. + * + * From the perspective of dealing with error handling, deferred and promise APIs are to + * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. + * + *
+ *   // for the purpose of this example let's assume that variables `$q` and `scope` are
+ *   // available in the current lexical scope (they could have been injected or passed in).
+ *
+ *   function asyncGreet(name) {
+ *     var deferred = $q.defer();
+ *
+ *     setTimeout(function() {
+ *       // since this fn executes async in a future turn of the event loop, we need to wrap
+ *       // our code into an $apply call so that the model changes are properly observed.
+ *       scope.$apply(function() {
+ *         if (okToGreet(name)) {
+ *           deferred.resolve('Hello, ' + name + '!');
+ *         } else {
+ *           deferred.reject('Greeting ' + name + ' is not allowed.');
+ *         }
+ *       });
+ *     }, 1000);
+ *
+ *     return deferred.promise;
+ *   }
+ *
+ *   var promise = asyncGreet('Robin Hood');
+ *   promise.then(function(greeting) {
+ *     alert('Success: ' + greeting);
+ *   }, function(reason) {
+ *     alert('Failed: ' + reason);
+ *   });
+ * 
+ * + * At first it might not be obvious why this extra complexity is worth the trouble. The payoff + * comes in the way of + * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). + * + * Additionally the promise api allows for composition that is very hard to do with the + * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. + * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the + * section on serial or parallel joining of promises. + * + * + * # The Deferred API + * + * A new instance of deferred is constructed by calling `$q.defer()`. + * + * The purpose of the deferred object is to expose the associated Promise instance as well as APIs + * that can be used for signaling the successful or unsuccessful completion of the task. + * + * **Methods** + * + * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection + * constructed via `$q.reject`, the promise will be rejected instead. + * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to + * resolving it with a rejection constructed via `$q.reject`. + * + * **Properties** + * + * - promise – `{Promise}` – promise object associated with this deferred. + * + * + * # The Promise API + * + * A new promise instance is created when a deferred instance is created and can be retrieved by + * calling `deferred.promise`. + * + * The purpose of the promise object is to allow for interested parties to get access to the result + * of the deferred task when it completes. + * + * **Methods** + * + * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved + * or rejected calls one of the success or error callbacks asynchronously as soon as the result + * is available. The callbacks are called with a single argument the result or rejection reason. + * + * This method *returns a new promise* which is resolved or rejected via the return value of the + * `successCallback` or `errorCallback`. + * + * - `always(callback)` – allows you to observe either the fulfillment or rejection of a promise, + * but to do so without modifying the final value. This is useful to release resources or do some + * clean-up that needs to be done whether the promise was rejected or resolved. See the [full + * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for + * more information. + * + * # Chaining promises + * + * Because calling `then` api of a promise returns a new derived promise, it is easily possible + * to create a chain of promises: + * + *
+ *   promiseB = promiseA.then(function(result) {
+ *     return result + 1;
+ *   });
+ *
+ *   // promiseB will be resolved immediately after promiseA is resolved and its value will be
+ *   // the result of promiseA incremented by 1
+ * 
+ * + * It is possible to create chains of any length and since a promise can be resolved with another + * promise (which will defer its resolution further), it is possible to pause/defer resolution of + * the promises at any point in the chain. This makes it possible to implement powerful apis like + * $http's response interceptors. + * + * + * # Differences between Kris Kowal's Q and $q + * + * There are three main differences: + * + * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation + * mechanism in angular, which means faster propagation of resolution or rejection into your + * models and avoiding unnecessary browser repaints, which would result in flickering UI. + * - $q promises are recognized by the templating engine in angular, which means that in templates + * you can treat promises attached to a scope as if they were the resulting values. + * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains + * all the important functionality needed for common async tasks. + * + * # Testing + * + *
+ *    it('should simulate promise', inject(function($q, $rootScope) {
+ *      var deferred = $q.defer();
+ *      var promise = deferred.promise;
+ *      var resolvedValue;
+ * 
+ *      promise.then(function(value) { resolvedValue = value; });
+ *      expect(resolvedValue).toBeUndefined();
+ * 
+ *      // Simulate resolving of promise
+ *      deferred.resolve(123);
+ *      // Note that the 'then' function does not get called synchronously.
+ *      // This is because we want the promise API to always be async, whether or not
+ *      // it got called synchronously or asynchronously.
+ *      expect(resolvedValue).toBeUndefined();
+ * 
+ *      // Propagate promise resolution to 'then' functions using $apply().
+ *      $rootScope.$apply();
+ *      expect(resolvedValue).toEqual(123);
+ *    });
+ *  
+ */ +function $QProvider() { + + this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { + return qFactory(function(callback) { + $rootScope.$evalAsync(callback); + }, $exceptionHandler); + }]; +} + + +/** + * Constructs a promise manager. + * + * @param {function(function)} nextTick Function for executing functions in the next turn. + * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + * debugging purposes. + * @returns {object} Promise manager. + */ +function qFactory(nextTick, exceptionHandler) { + + /** + * @ngdoc + * @name ng.$q#defer + * @methodOf ng.$q + * @description + * Creates a `Deferred` object which represents a task which will finish in the future. + * + * @returns {Deferred} Returns a new instance of deferred. + */ + var defer = function() { + var pending = [], + value, deferred; + + deferred = { + + resolve: function(val) { + if (pending) { + var callbacks = pending; + pending = undefined; + value = ref(val); + + if (callbacks.length) { + nextTick(function() { + var callback; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + callback = callbacks[i]; + value.then(callback[0], callback[1]); + } + }); + } + } + }, + + + reject: function(reason) { + deferred.resolve(reject(reason)); + }, + + + promise: { + then: function(callback, errback) { + var result = defer(); + + var wrappedCallback = function(value) { + try { + result.resolve((callback || defaultCallback)(value)); + } catch(e) { + exceptionHandler(e); + result.reject(e); + } + }; + + var wrappedErrback = function(reason) { + try { + result.resolve((errback || defaultErrback)(reason)); + } catch(e) { + exceptionHandler(e); + result.reject(e); + } + }; + + if (pending) { + pending.push([wrappedCallback, wrappedErrback]); + } else { + value.then(wrappedCallback, wrappedErrback); + } + + return result.promise; + }, + always: function(callback) { + + function makePromise(value, resolved) { + var result = defer(); + if (resolved) { + result.resolve(value); + } else { + result.reject(value); + } + return result.promise; + } + + function handleCallback(value, isResolved) { + var callbackOutput = null; + try { + callbackOutput = (callback ||defaultCallback)(); + } catch(e) { + return makePromise(e, false); + } + if (callbackOutput && callbackOutput.then) { + return callbackOutput.then(function() { + return makePromise(value, isResolved); + }, function(error) { + return makePromise(error, false); + }); + } else { + return makePromise(value, isResolved); + } + } + + return this.then(function(value) { + return handleCallback(value, true); + }, function(error) { + return handleCallback(error, false); + }); + } + } + }; + + return deferred; + }; + + + var ref = function(value) { + if (value && value.then) return value; + return { + then: function(callback) { + var result = defer(); + nextTick(function() { + result.resolve(callback(value)); + }); + return result.promise; + } + }; + }; + + + /** + * @ngdoc + * @name ng.$q#reject + * @methodOf ng.$q + * @description + * Creates a promise that is resolved as rejected with the specified `reason`. This api should be + * used to forward rejection in a chain of promises. If you are dealing with the last promise in + * a promise chain, you don't need to worry about it. + * + * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of + * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via + * a promise error callback and you want to forward the error to the promise derived from the + * current promise, you have to "rethrow" the error by returning a rejection constructed via + * `reject`. + * + *
+   *   promiseB = promiseA.then(function(result) {
+   *     // success: do something and resolve promiseB
+   *     //          with the old or a new result
+   *     return result;
+   *   }, function(reason) {
+   *     // error: handle the error if possible and
+   *     //        resolve promiseB with newPromiseOrValue,
+   *     //        otherwise forward the rejection to promiseB
+   *     if (canHandle(reason)) {
+   *      // handle the error and recover
+   *      return newPromiseOrValue;
+   *     }
+   *     return $q.reject(reason);
+   *   });
+   * 
+ * + * @param {*} reason Constant, message, exception or an object representing the rejection reason. + * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. + */ + var reject = function(reason) { + return { + then: function(callback, errback) { + var result = defer(); + nextTick(function() { + result.resolve((errback || defaultErrback)(reason)); + }); + return result.promise; + } + }; + }; + + + /** + * @ngdoc + * @name ng.$q#when + * @methodOf ng.$q + * @description + * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. + * This is useful when you are dealing with an object that might or might not be a promise, or if + * the promise comes from a source that can't be trusted. + * + * @param {*} value Value or a promise + * @returns {Promise} Returns a promise of the passed value or promise + */ + var when = function(value, callback, errback) { + var result = defer(), + done; + + var wrappedCallback = function(value) { + try { + return (callback || defaultCallback)(value); + } catch (e) { + exceptionHandler(e); + return reject(e); + } + }; + + var wrappedErrback = function(reason) { + try { + return (errback || defaultErrback)(reason); + } catch (e) { + exceptionHandler(e); + return reject(e); + } + }; + + nextTick(function() { + ref(value).then(function(value) { + if (done) return; + done = true; + result.resolve(ref(value).then(wrappedCallback, wrappedErrback)); + }, function(reason) { + if (done) return; + done = true; + result.resolve(wrappedErrback(reason)); + }); + }); + + return result.promise; + }; + + + function defaultCallback(value) { + return value; + } + + + function defaultErrback(reason) { + return reject(reason); + } + + + /** + * @ngdoc + * @name ng.$q#all + * @methodOf ng.$q + * @description + * Combines multiple promises into a single promise that is resolved when all of the input + * promises are resolved. + * + * @param {Array.|Object.} promises An array or hash of promises. + * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, + * each value corresponding to the promise at the same index/key in the `promises` array/hash. If any of + * the promises is resolved with a rejection, this resulting promise will be resolved with the + * same rejection. + */ + function all(promises) { + var deferred = defer(), + counter = 0, + results = isArray(promises) ? [] : {}; + + forEach(promises, function(promise, key) { + counter++; + ref(promise).then(function(value) { + if (results.hasOwnProperty(key)) return; + results[key] = value; + if (!(--counter)) deferred.resolve(results); + }, function(reason) { + if (results.hasOwnProperty(key)) return; + deferred.reject(reason); + }); + }); + + if (counter === 0) { + deferred.resolve(results); + } + + return deferred.promise; + } + + return { + defer: defer, + reject: reject, + when: when, + all: all + }; +} + +/** + * @ngdoc object + * @name ng.$routeProvider + * @function + * + * @description + * + * Used for configuring routes. See {@link ng.$route $route} for an example. + */ +function $RouteProvider(){ + var routes = {}; + + /** + * @ngdoc method + * @name ng.$routeProvider#when + * @methodOf ng.$routeProvider + * + * @param {string} path Route path (matched against `$location.path`). If `$location.path` + * contains redundant trailing slash or is missing one, the route will still match and the + * `$location.path` will be updated to add or drop the trailing slash to exactly match the + * route definition. + * + * * `path` can contain named groups starting with a colon (`:name`). All characters up + * to the next slash are matched and stored in `$routeParams` under the given `name` + * when the route matches. + * * `path` can contain named groups starting with a star (`*name`). All characters are + * eagerly stored in `$routeParams` under the given `name` when the route matches. + * + * For example, routes like `/color/:color/largecode/*largecode/edit` will match + * `/color/brown/largecode/code/with/slashs/edit` and extract: + * + * * `color: brown` + * * `largecode: code/with/slashs`. + * + * + * @param {Object} route Mapping information to be assigned to `$route.current` on route + * match. + * + * Object properties: + * + * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly + * created scope or the name of a {@link angular.Module#controller registered controller} + * if passed as a string. + * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be + * published to scope under the `controllerAs` name. + * - `template` – `{string=|function()=}` – html template as a string or function that returns + * an html template as a string which should be used by {@link ng.directive:ngView ngView} or + * {@link ng.directive:ngInclude ngInclude} directives. + * This property takes precedence over `templateUrl`. + * + * If `template` is a function, it will be called with the following parameters: + * + * - `{Array.}` - route parameters extracted from the current + * `$location.path()` by applying the current route + * + * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html + * template that should be used by {@link ng.directive:ngView ngView}. + * + * If `templateUrl` is a function, it will be called with the following parameters: + * + * - `{Array.}` - route parameters extracted from the current + * `$location.path()` by applying the current route + * + * - `resolve` - `{Object.=}` - An optional map of dependencies which should + * be injected into the controller. If any of these dependencies are promises, they will be + * resolved and converted to a value before the controller is instantiated and the + * `$routeChangeSuccess` event is fired. The map object is: + * + * - `key` – `{string}`: a name of a dependency to be injected into the controller. + * - `factory` - `{string|function}`: If `string` then it is an alias for a service. + * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected} + * and the return value is treated as the dependency. If the result is a promise, it is resolved + * before its value is injected into the controller. + * + * - `redirectTo` – {(string|function())=} – value to update + * {@link ng.$location $location} path with and trigger route redirection. + * + * If `redirectTo` is a function, it will be called with the following parameters: + * + * - `{Object.}` - route parameters extracted from the current + * `$location.path()` by applying the current route templateUrl. + * - `{string}` - current `$location.path()` + * - `{Object}` - current `$location.search()` + * + * The custom `redirectTo` function is expected to return a string which will be used + * to update `$location.path()` and `$location.search()`. + * + * - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() + * changes. + * + * If the option is set to `false` and url in the browser changes, then + * `$routeUpdate` event is broadcasted on the root scope. + * + * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive + * + * If the option is set to `true`, then the particular route can be matched without being + * case sensitive + * + * @returns {Object} self + * + * @description + * Adds a new route definition to the `$route` service. + */ + this.when = function(path, route) { + routes[path] = extend({reloadOnSearch: true, caseInsensitiveMatch: false}, route); + + // create redirection for trailing slashes + if (path) { + var redirectPath = (path[path.length-1] == '/') + ? path.substr(0, path.length-1) + : path +'/'; + + routes[redirectPath] = {redirectTo: path}; + } + + return this; + }; + + /** + * @ngdoc method + * @name ng.$routeProvider#otherwise + * @methodOf ng.$routeProvider + * + * @description + * Sets route definition that will be used on route change when no other route definition + * is matched. + * + * @param {Object} params Mapping information to be assigned to `$route.current`. + * @returns {Object} self + */ + this.otherwise = function(params) { + this.when(null, params); + return this; + }; + + + this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', + function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) { + + /** + * @ngdoc object + * @name ng.$route + * @requires $location + * @requires $routeParams + * + * @property {Object} current Reference to the current route definition. + * The route definition contains: + * + * - `controller`: The controller constructor as define in route definition. + * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for + * controller instantiation. The `locals` contain + * the resolved values of the `resolve` map. Additionally the `locals` also contain: + * + * - `$scope` - The current route scope. + * - `$template` - The current route template HTML. + * + * @property {Array.} routes Array of all configured routes. + * + * @description + * Is used for deep-linking URLs to controllers and views (HTML partials). + * It watches `$location.url()` and tries to map the path to an existing route definition. + * + * You can define routes through {@link ng.$routeProvider $routeProvider}'s API. + * + * The `$route` service is typically used in conjunction with {@link ng.directive:ngView ngView} + * directive and the {@link ng.$routeParams $routeParams} service. + * + * @example + This example shows how changing the URL hash causes the `$route` to match a route against the + URL, and the `ngView` pulls in the partial. + + Note that this example is using {@link ng.directive:script inlined templates} + to get it working on jsfiddle as well. + + + +
+ Choose: + Moby | + Moby: Ch1 | + Gatsby | + Gatsby: Ch4 | + Scarlet Letter
+ +
+
+ +
$location.path() = {{$location.path()}}
+
$route.current.templateUrl = {{$route.current.templateUrl}}
+
$route.current.params = {{$route.current.params}}
+
$route.current.scope.name = {{$route.current.scope.name}}
+
$routeParams = {{$routeParams}}
+
+
+ + + controller: {{name}}
+ Book Id: {{params.bookId}}
+
+ + + controller: {{name}}
+ Book Id: {{params.bookId}}
+ Chapter Id: {{params.chapterId}} +
+ + + angular.module('ngView', [], function($routeProvider, $locationProvider) { + $routeProvider.when('/Book/:bookId', { + templateUrl: 'book.html', + controller: BookCntl, + resolve: { + // I will cause a 1 second delay + delay: function($q, $timeout) { + var delay = $q.defer(); + $timeout(delay.resolve, 1000); + return delay.promise; + } + } + }); + $routeProvider.when('/Book/:bookId/ch/:chapterId', { + templateUrl: 'chapter.html', + controller: ChapterCntl + }); + + // configure html5 to get links working on jsfiddle + $locationProvider.html5Mode(true); + }); + + function MainCntl($scope, $route, $routeParams, $location) { + $scope.$route = $route; + $scope.$location = $location; + $scope.$routeParams = $routeParams; + } + + function BookCntl($scope, $routeParams) { + $scope.name = "BookCntl"; + $scope.params = $routeParams; + } + + function ChapterCntl($scope, $routeParams) { + $scope.name = "ChapterCntl"; + $scope.params = $routeParams; + } + + + + it('should load and compile correct template', function() { + element('a:contains("Moby: Ch1")').click(); + var content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: ChapterCntl/); + expect(content).toMatch(/Book Id\: Moby/); + expect(content).toMatch(/Chapter Id\: 1/); + + element('a:contains("Scarlet")').click(); + sleep(2); // promises are not part of scenario waiting + content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: BookCntl/); + expect(content).toMatch(/Book Id\: Scarlet/); + }); + +
+ */ + + /** + * @ngdoc event + * @name ng.$route#$routeChangeStart + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted before a route change. At this point the route services starts + * resolving all of the dependencies needed for the route change to occurs. + * Typically this involves fetching the view template as well as any dependencies + * defined in `resolve` route property. Once all of the dependencies are resolved + * `$routeChangeSuccess` is fired. + * + * @param {Route} next Future route information. + * @param {Route} current Current route information. + */ + + /** + * @ngdoc event + * @name ng.$route#$routeChangeSuccess + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted after a route dependencies are resolved. + * {@link ng.directive:ngView ngView} listens for the directive + * to instantiate the controller and render the view. + * + * @param {Object} angularEvent Synthetic event object. + * @param {Route} current Current route information. + * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered. + */ + + /** + * @ngdoc event + * @name ng.$route#$routeChangeError + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted if any of the resolve promises are rejected. + * + * @param {Route} current Current route information. + * @param {Route} previous Previous route information. + * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. + */ + + /** + * @ngdoc event + * @name ng.$route#$routeUpdate + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * + * The `reloadOnSearch` property has been set to false, and we are reusing the same + * instance of the Controller. + */ + + var forceReload = false, + $route = { + routes: routes, + + /** + * @ngdoc method + * @name ng.$route#reload + * @methodOf ng.$route + * + * @description + * Causes `$route` service to reload the current route even if + * {@link ng.$location $location} hasn't changed. + * + * As a result of that, {@link ng.directive:ngView ngView} + * creates new scope, reinstantiates the controller. + */ + reload: function() { + forceReload = true; + $rootScope.$evalAsync(updateRoute); + } + }; + + $rootScope.$on('$locationChangeSuccess', updateRoute); + + return $route; + + ///////////////////////////////////////////////////// + + /** + * @param on {string} current url + * @param when {string} route when template to match the url against + * @param whenProperties {Object} properties to define when's matching behavior + * @return {?Object} + */ + function switchRouteMatcher(on, when, whenProperties) { + // TODO(i): this code is convoluted and inefficient, we should construct the route matching + // regex only once and then reuse it + + // Escape regexp special characters. + when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$'; + + var regex = '', + params = [], + dst = {}; + + var re = /\\([:*])(\w+)/g, + paramMatch, + lastMatchedIndex = 0; + + while ((paramMatch = re.exec(when)) !== null) { + // Find each :param in `when` and replace it with a capturing group. + // Append all other sections of when unchanged. + regex += when.slice(lastMatchedIndex, paramMatch.index); + switch(paramMatch[1]) { + case ':': + regex += '([^\\/]*)'; + break; + case '*': + regex += '(.*)'; + break; + } + params.push(paramMatch[2]); + lastMatchedIndex = re.lastIndex; + } + // Append trailing path part. + regex += when.substr(lastMatchedIndex); + + var match = on.match(new RegExp(regex, whenProperties.caseInsensitiveMatch ? 'i' : '')); + if (match) { + forEach(params, function(name, index) { + dst[name] = match[index + 1]; + }); + } + return match ? dst : null; + } + + function updateRoute() { + var next = parseRoute(), + last = $route.current; + + if (next && last && next.$$route === last.$$route + && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) { + last.params = next.params; + copy(last.params, $routeParams); + $rootScope.$broadcast('$routeUpdate', last); + } else if (next || last) { + forceReload = false; + $rootScope.$broadcast('$routeChangeStart', next, last); + $route.current = next; + if (next) { + if (next.redirectTo) { + if (isString(next.redirectTo)) { + $location.path(interpolate(next.redirectTo, next.params)).search(next.params) + .replace(); + } else { + $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) + .replace(); + } + } + } + + $q.when(next). + then(function() { + if (next) { + var locals = extend({}, next.resolve), + template; + + forEach(locals, function(value, key) { + locals[key] = isString(value) ? $injector.get(value) : $injector.invoke(value); + }); + + if (isDefined(template = next.template)) { + if (isFunction(template)) { + template = template(next.params); + } + } else if (isDefined(template = next.templateUrl)) { + if (isFunction(template)) { + template = template(next.params); + } + if (isDefined(template)) { + next.loadedTemplateUrl = template; + template = $http.get(template, {cache: $templateCache}). + then(function(response) { return response.data; }); + } + } + if (isDefined(template)) { + locals['$template'] = template; + } + return $q.all(locals); + } + }). + // after route change + then(function(locals) { + if (next == $route.current) { + if (next) { + next.locals = locals; + copy(next.params, $routeParams); + } + $rootScope.$broadcast('$routeChangeSuccess', next, last); + } + }, function(error) { + if (next == $route.current) { + $rootScope.$broadcast('$routeChangeError', next, last, error); + } + }); + } + } + + + /** + * @returns the current active route, by matching it against the URL + */ + function parseRoute() { + // Match a route + var params, match; + forEach(routes, function(route, path) { + if (!match && (params = switchRouteMatcher($location.path(), path, route))) { + match = inherit(route, { + params: extend({}, $location.search(), params), + pathParams: params}); + match.$$route = route; + } + }); + // No route matched; fallback to "otherwise" route + return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); + } + + /** + * @returns interpolation of the redirect path with the parameters + */ + function interpolate(string, params) { + var result = []; + forEach((string||'').split(':'), function(segment, i) { + if (i == 0) { + result.push(segment); + } else { + var segmentMatch = segment.match(/(\w+)(.*)/); + var key = segmentMatch[1]; + result.push(params[key]); + result.push(segmentMatch[2] || ''); + delete params[key]; + } + }); + return result.join(''); + } + }]; +} + +/** + * @ngdoc object + * @name ng.$routeParams + * @requires $route + * + * @description + * Current set of route parameters. The route parameters are a combination of the + * {@link ng.$location $location} `search()`, and `path()`. The `path` parameters + * are extracted when the {@link ng.$route $route} path is matched. + * + * In case of parameter name collision, `path` params take precedence over `search` params. + * + * The service guarantees that the identity of the `$routeParams` object will remain unchanged + * (but its properties will likely change) even when a route change occurs. + * + * @example + *
+ *  // Given:
+ *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
+ *  // Route: /Chapter/:chapterId/Section/:sectionId
+ *  //
+ *  // Then
+ *  $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
+ * 
+ */ +function $RouteParamsProvider() { + this.$get = valueFn({}); +} + +/** + * DESIGN NOTES + * + * The design decisions behind the scope are heavily favored for speed and memory consumption. + * + * The typical use of scope is to watch the expressions, which most of the time return the same + * value as last time so we optimize the operation. + * + * Closures construction is expensive in terms of speed as well as memory: + * - No closures, instead use prototypical inheritance for API + * - Internal state needs to be stored on scope directly, which means that private state is + * exposed as $$____ properties + * + * Loop operations are optimized by using while(count--) { ... } + * - this means that in order to keep the same order of execution as addition we have to add + * items to the array at the beginning (shift) instead of at the end (push) + * + * Child scopes are created and removed often + * - Using an array would be slow since inserts in middle are expensive so we use linked list + * + * There are few watches then a lot of observers. This is why you don't want the observer to be + * implemented in the same way as watch. Watch requires return of initialization function which + * are expensive to construct. + */ + + +/** + * @ngdoc object + * @name ng.$rootScopeProvider + * @description + * + * Provider for the $rootScope service. + */ + +/** + * @ngdoc function + * @name ng.$rootScopeProvider#digestTtl + * @methodOf ng.$rootScopeProvider + * @description + * + * Sets the number of digest iterations the scope should attempt to execute before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * @param {number} limit The number of digest iterations. + */ + + +/** + * @ngdoc object + * @name ng.$rootScope + * @description + * + * Every application has a single root {@link ng.$rootScope.Scope scope}. + * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide + * event processing life-cycle. See {@link guide/scope developer guide on scopes}. + */ +function $RootScopeProvider(){ + var TTL = 10; + + this.digestTtl = function(value) { + if (arguments.length) { + TTL = value; + } + return TTL; + }; + + this.$get = ['$injector', '$exceptionHandler', '$parse', + function( $injector, $exceptionHandler, $parse) { + + /** + * @ngdoc function + * @name ng.$rootScope.Scope + * + * @description + * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the + * {@link AUTO.$injector $injector}. Child scopes are created using the + * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when + * compiled HTML template is executed.) + * + * Here is a simple scope snippet to show how you can interact with the scope. + *
+     * 
+     * 
+ * + * # Inheritance + * A scope can inherit from a parent scope, as in this example: + *
+         var parent = $rootScope;
+         var child = parent.$new();
+
+         parent.salutation = "Hello";
+         child.name = "World";
+         expect(child.salutation).toEqual('Hello');
+
+         child.salutation = "Welcome";
+         expect(child.salutation).toEqual('Welcome');
+         expect(parent.salutation).toEqual('Hello');
+     * 
+ * + * + * @param {Object.=} providers Map of service factory which need to be provided + * for the current scope. Defaults to {@link ng}. + * @param {Object.=} instanceCache Provides pre-instantiated services which should + * append/override services provided by `providers`. This is handy when unit-testing and having + * the need to override a default service. + * @returns {Object} Newly created scope. + * + */ + function Scope() { + this.$id = nextUid(); + this.$$phase = this.$parent = this.$$watchers = + this.$$nextSibling = this.$$prevSibling = + this.$$childHead = this.$$childTail = null; + this['this'] = this.$root = this; + this.$$destroyed = false; + this.$$asyncQueue = []; + this.$$listeners = {}; + this.$$isolateBindings = {}; + } + + /** + * @ngdoc property + * @name ng.$rootScope.Scope#$id + * @propertyOf ng.$rootScope.Scope + * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for + * debugging. + */ + + + Scope.prototype = { + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$new + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Creates a new child {@link ng.$rootScope.Scope scope}. + * + * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and + * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope + * hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. + * + * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for + * the scope and its child scopes to be permanently detached from the parent and thus stop + * participating in model change detection and listener notification by invoking. + * + * @param {boolean} isolate if true then the scope does not prototypically inherit from the + * parent scope. The scope is isolated, as it can not see parent scope properties. + * When creating widgets it is useful for the widget to not accidentally read parent + * state. + * + * @returns {Object} The newly created child scope. + * + */ + $new: function(isolate) { + var Child, + child; + + if (isFunction(isolate)) { + // TODO: remove at some point + throw Error('API-CHANGE: Use $controller to instantiate controllers.'); + } + if (isolate) { + child = new Scope(); + child.$root = this.$root; + } else { + Child = function() {}; // should be anonymous; This is so that when the minifier munges + // the name it does not become random set of chars. These will then show up as class + // name in the debugger. + Child.prototype = this; + child = new Child(); + child.$id = nextUid(); + } + child['this'] = child; + child.$$listeners = {}; + child.$parent = this; + child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null; + child.$$prevSibling = this.$$childTail; + if (this.$$childHead) { + this.$$childTail.$$nextSibling = child; + this.$$childTail = child; + } else { + this.$$childHead = this.$$childTail = child; + } + return child; + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$watch + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Registers a `listener` callback to be executed whenever the `watchExpression` changes. + * + * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest $digest()} and + * should return the value which will be watched. (Since {@link ng.$rootScope.Scope#$digest $digest()} + * reruns when it detects changes the `watchExpression` can execute multiple times per + * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) + * - The `listener` is called only when the value from the current `watchExpression` and the + * previous call to `watchExpression` are not equal (with the exception of the initial run, + * see below). The inequality is determined according to + * {@link angular.equals} function. To save the value of the object for later comparison, the + * {@link angular.copy} function is used. It also means that watching complex options will + * have adverse memory and performance implications. + * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This + * is achieved by rerunning the watchers until no changes are detected. The rerun iteration + * limit is 10 to prevent an infinite loop deadlock. + * + * + * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, + * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` + * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is + * detected, be prepared for multiple calls to your listener.) + * + * After a watcher is registered with the scope, the `listener` fn is called asynchronously + * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the + * watcher. In rare cases, this is undesirable because the listener is called when the result + * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you + * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the + * listener was called due to initialization. + * + * + * # Example + *
+           // let's assume that scope was dependency injected as the $rootScope
+           var scope = $rootScope;
+           scope.name = 'misko';
+           scope.counter = 0;
+
+           expect(scope.counter).toEqual(0);
+           scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
+           expect(scope.counter).toEqual(0);
+
+           scope.$digest();
+           // no variable change
+           expect(scope.counter).toEqual(0);
+
+           scope.name = 'adam';
+           scope.$digest();
+           expect(scope.counter).toEqual(1);
+       * 
+ * + * + * + * @param {(function()|string)} watchExpression Expression that is evaluated on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a + * call to the `listener`. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(scope)`: called with current `scope` as a parameter. + * @param {(function()|string)=} listener Callback called whenever the return value of + * the `watchExpression` changes. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. + * + * @param {boolean=} objectEquality Compare object for equality rather than for reference. + * @returns {function()} Returns a deregistration function for this listener. + */ + $watch: function(watchExp, listener, objectEquality) { + var scope = this, + get = compileToFn(watchExp, 'watch'), + array = scope.$$watchers, + watcher = { + fn: listener, + last: initWatchVal, + get: get, + exp: watchExp, + eq: !!objectEquality + }; + + // in the case user pass string, we need to compile it, do we really need this ? + if (!isFunction(listener)) { + var listenFn = compileToFn(listener || noop, 'listener'); + watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; + } + + if (typeof watchExp == 'string' && get.constant) { + var originalFn = watcher.fn; + watcher.fn = function(newVal, oldVal, scope) { + originalFn.call(this, newVal, oldVal, scope); + arrayRemove(array, watcher); + }; + } + + if (!array) { + array = scope.$$watchers = []; + } + // we use unshift since we use a while loop in $digest for speed. + // the while loop reads in reverse order. + array.unshift(watcher); + + return function() { + arrayRemove(array, watcher); + }; + }, + + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$watchCollection + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Shallow watches the properties of an object and fires whenever any of the properties change + * (for arrays this implies watching the array items, for object maps this implies watching the properties). + * If a change is detected the `listener` callback is fired. + * + * - The `obj` collection is observed via standard $watch operation and is examined on every call to $digest() to + * see if any items have been added, removed, or moved. + * - The `listener` is called whenever anything within the `obj` has changed. Examples include adding new items + * into the object or array, removing and moving items around. + * + * + * # Example + *
+          $scope.names = ['igor', 'matias', 'misko', 'james'];
+          $scope.dataCount = 4;
+
+          $scope.$watchCollection('names', function(newNames, oldNames) {
+            $scope.dataCount = newNames.length;
+          });
+
+          expect($scope.dataCount).toEqual(4);
+          $scope.$digest();
+
+          //still at 4 ... no changes
+          expect($scope.dataCount).toEqual(4);
+
+          $scope.names.pop();
+          $scope.$digest();
+
+          //now there's been a change
+          expect($scope.dataCount).toEqual(3);
+       * 
+ * + * + * @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The expression value + * should evaluate to an object or an array which is observed on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the collection will trigger + * a call to the `listener`. + * + * @param {function(newCollection, oldCollection, scope)} listener a callback function that is fired with both + * the `newCollection` and `oldCollection` as parameters. + * The `newCollection` object is the newly modified data obtained from the `obj` expression and the + * `oldCollection` object is a copy of the former collection data. + * The `scope` refers to the current scope. + * + * @returns {function()} Returns a de-registration function for this listener. When the de-registration function is executed + * then the internal watch operation is terminated. + */ + $watchCollection: function(obj, listener) { + var self = this; + var oldValue; + var newValue; + var changeDetected = 0; + var objGetter = $parse(obj); + var internalArray = []; + var internalObject = {}; + var oldLength = 0; + + function $watchCollectionWatch() { + newValue = objGetter(self); + var newLength, key; + + if (!isObject(newValue)) { + if (oldValue !== newValue) { + oldValue = newValue; + changeDetected++; + } + } else if (isArrayLike(newValue)) { + if (oldValue !== internalArray) { + // we are transitioning from something which was not an array into array. + oldValue = internalArray; + oldLength = oldValue.length = 0; + changeDetected++; + } + + newLength = newValue.length; + + if (oldLength !== newLength) { + // if lengths do not match we need to trigger change notification + changeDetected++; + oldValue.length = oldLength = newLength; + } + // copy the items to oldValue and look for changes. + for (var i = 0; i < newLength; i++) { + if (oldValue[i] !== newValue[i]) { + changeDetected++; + oldValue[i] = newValue[i]; + } + } + } else { + if (oldValue !== internalObject) { + // we are transitioning from something which was not an object into object. + oldValue = internalObject = {}; + oldLength = 0; + changeDetected++; + } + // copy the items to oldValue and look for changes. + newLength = 0; + for (key in newValue) { + if (newValue.hasOwnProperty(key)) { + newLength++; + if (oldValue.hasOwnProperty(key)) { + if (oldValue[key] !== newValue[key]) { + changeDetected++; + oldValue[key] = newValue[key]; + } + } else { + oldLength++; + oldValue[key] = newValue[key]; + changeDetected++; + } + } + } + if (oldLength > newLength) { + // we used to have more keys, need to find them and destroy them. + changeDetected++; + for(key in oldValue) { + if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) { + oldLength--; + delete oldValue[key]; + } + } + } + } + return changeDetected; + } + + function $watchCollectionAction() { + listener(newValue, oldValue, self); + } + + return this.$watch($watchCollectionWatch, $watchCollectionAction); + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$digest + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children. + * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the + * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are + * firing. This means that it is possible to get into an infinite loop. This function will throw + * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10. + * + * Usually you don't call `$digest()` directly in + * {@link ng.directive:ngController controllers} or in + * {@link ng.$compileProvider#directive directives}. + * Instead a call to {@link ng.$rootScope.Scope#$apply $apply()} (typically from within a + * {@link ng.$compileProvider#directive directives}) will force a `$digest()`. + * + * If you want to be notified whenever `$digest()` is called, + * you can register a `watchExpression` function with {@link ng.$rootScope.Scope#$watch $watch()} + * with no `listener`. + * + * You may have a need to call `$digest()` from within unit-tests, to simulate the scope + * life-cycle. + * + * # Example + *
+           var scope = ...;
+           scope.name = 'misko';
+           scope.counter = 0;
+
+           expect(scope.counter).toEqual(0);
+           scope.$watch('name', function(newValue, oldValue) {
+             scope.counter = scope.counter + 1;
+           });
+           expect(scope.counter).toEqual(0);
+
+           scope.$digest();
+           // no variable change
+           expect(scope.counter).toEqual(0);
+
+           scope.name = 'adam';
+           scope.$digest();
+           expect(scope.counter).toEqual(1);
+       * 
+ * + */ + $digest: function() { + var watch, value, last, + watchers, + asyncQueue = this.$$asyncQueue, + length, + dirty, ttl = TTL, + next, current, target = this, + watchLog = [], + logIdx, logMsg; + + beginPhase('$digest'); + + do { // "while dirty" loop + dirty = false; + current = target; + + while(asyncQueue.length) { + try { + current.$eval(asyncQueue.shift()); + } catch (e) { + $exceptionHandler(e); + } + } + + do { // "traverse the scopes" loop + if ((watchers = current.$$watchers)) { + // process our watches + length = watchers.length; + while (length--) { + try { + watch = watchers[length]; + // Most common watches are on primitives, in which case we can short + // circuit it with === operator, only when === fails do we use .equals + if ((value = watch.get(current)) !== (last = watch.last) && + !(watch.eq + ? equals(value, last) + : (typeof value == 'number' && typeof last == 'number' + && isNaN(value) && isNaN(last)))) { + dirty = true; + watch.last = watch.eq ? copy(value) : value; + watch.fn(value, ((last === initWatchVal) ? value : last), current); + if (ttl < 5) { + logIdx = 4 - ttl; + if (!watchLog[logIdx]) watchLog[logIdx] = []; + logMsg = (isFunction(watch.exp)) + ? 'fn: ' + (watch.exp.name || watch.exp.toString()) + : watch.exp; + logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); + watchLog[logIdx].push(logMsg); + } + } + } catch (e) { + $exceptionHandler(e); + } + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $broadcast + if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { + while(current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } while ((current = next)); + + if(dirty && !(ttl--)) { + clearPhase(); + throw Error(TTL + ' $digest() iterations reached. Aborting!\n' + + 'Watchers fired in the last 5 iterations: ' + toJson(watchLog)); + } + } while (dirty || asyncQueue.length); + + clearPhase(); + }, + + + /** + * @ngdoc event + * @name ng.$rootScope.Scope#$destroy + * @eventOf ng.$rootScope.Scope + * @eventType broadcast on scope being destroyed + * + * @description + * Broadcasted when a scope and its children are being destroyed. + */ + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$destroy + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Removes the current scope (and all of its children) from the parent scope. Removal implies + * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer + * propagate to the current scope and its children. Removal also implies that the current + * scope is eligible for garbage collection. + * + * The `$destroy()` is usually used by directives such as + * {@link ng.directive:ngRepeat ngRepeat} for managing the + * unrolling of the loop. + * + * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope. + * Application code can register a `$destroy` event handler that will give it chance to + * perform any necessary cleanup. + */ + $destroy: function() { + // we can't destroy the root scope or a scope that has been already destroyed + if ($rootScope == this || this.$$destroyed) return; + var parent = this.$parent; + + this.$broadcast('$destroy'); + this.$$destroyed = true; + + if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; + if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; + if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; + if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; + + // This is bogus code that works around Chrome's GC leak + // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = + this.$$childTail = null; + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$eval + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Executes the `expression` on the current scope returning the result. Any exceptions in the + * expression are propagated (uncaught). This is useful when evaluating Angular expressions. + * + * # Example + *
+           var scope = ng.$rootScope.Scope();
+           scope.a = 1;
+           scope.b = 2;
+
+           expect(scope.$eval('a+b')).toEqual(3);
+           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
+       * 
+ * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $eval: function(expr, locals) { + return $parse(expr)(this, locals); + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$evalAsync + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Executes the expression on the current scope at a later point in time. + * + * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that: + * + * - it will execute in the current script execution context (before any DOM rendering). + * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after + * `expression` execution. + * + * Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + */ + $evalAsync: function(expr) { + this.$$asyncQueue.push(expr); + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$apply + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * `$apply()` is used to execute an expression in angular from outside of the angular framework. + * (For example from browser DOM events, setTimeout, XHR or third party libraries). + * Because we are calling into the angular framework we need to perform proper scope life-cycle + * of {@link ng.$exceptionHandler exception handling}, + * {@link ng.$rootScope.Scope#$digest executing watches}. + * + * ## Life cycle + * + * # Pseudo-Code of `$apply()` + *
+           function $apply(expr) {
+             try {
+               return $eval(expr);
+             } catch (e) {
+               $exceptionHandler(e);
+             } finally {
+               $root.$digest();
+             }
+           }
+       * 
+ * + * + * Scope's `$apply()` method transitions through the following stages: + * + * 1. The {@link guide/expression expression} is executed using the + * {@link ng.$rootScope.Scope#$eval $eval()} method. + * 2. Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the expression + * was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. + * + * + * @param {(string|function())=} exp An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $apply: function(expr) { + try { + beginPhase('$apply'); + return this.$eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + clearPhase(); + try { + $rootScope.$digest(); + } catch (e) { + $exceptionHandler(e); + throw e; + } + } + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$on + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of + * event life cycle. + * + * The event listener function format is: `function(event, args...)`. The `event` object + * passed into the listener has the following attributes: + * + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed. + * - `currentScope` - `{Scope}`: the current scope which is handling the event. + * - `name` - `{string}`: Name of the event. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event + * propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true. + * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. + * + * @param {string} name Event name to listen on. + * @param {function(event, args...)} listener Function to call when the event is emitted. + * @returns {function()} Returns a deregistration function for this listener. + */ + $on: function(name, listener) { + var namedListeners = this.$$listeners[name]; + if (!namedListeners) { + this.$$listeners[name] = namedListeners = []; + } + namedListeners.push(listener); + + return function() { + namedListeners[indexOf(namedListeners, listener)] = null; + }; + }, + + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$emit + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Dispatches an event `name` upwards through the scope hierarchy notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$emit` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified. + * Afterwards, the event traverses upwards toward the root scope and calls all registered + * listeners along the way. The event will stop propagating if one of the listeners cancels it. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to emit. + * @param {...*} args Optional set of arguments which will be passed onto the event listeners. + * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} + */ + $emit: function(name, args) { + var empty = [], + namedListeners, + scope = this, + stopPropagation = false, + event = { + name: name, + targetScope: scope, + stopPropagation: function() {stopPropagation = true;}, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }, + listenerArgs = concat([event], arguments, 1), + i, length; + + do { + namedListeners = scope.$$listeners[name] || empty; + event.currentScope = scope; + for (i=0, length=namedListeners.length; i 7), + hasEvent: function(event) { + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + if (event == 'input' && msie == 9) return false; + + if (isUndefined(eventSupport[event])) { + var divElm = document.createElement('div'); + eventSupport[event] = 'on' + event in divElm; + } + + return eventSupport[event]; + }, + csp: document.securityPolicy ? document.securityPolicy.isActive : false, + vendorPrefix: vendorPrefix, + transitions : transitions, + animations : animations + }; + }]; +} + +/** + * @ngdoc object + * @name ng.$window + * + * @description + * A reference to the browser's `window` object. While `window` + * is globally available in JavaScript, it causes testability problems, because + * it is a global variable. In angular we always refer to it through the + * `$window` service, so it may be overridden, removed or mocked for testing. + * + * All expressions are evaluated with respect to current scope so they don't + * suffer from window globality. + * + * @example + + + +
+ + +
+
+ + it('should display the greeting in the input box', function() { + input('greeting').enter('Hello, E2E Tests'); + // If we click the button it will block the test runner + // element(':button').click(); + }); + +
+ */ +function $WindowProvider(){ + this.$get = valueFn(window); +} + +/** + * Parse headers into key value object + * + * @param {string} headers Raw headers as a string + * @returns {Object} Parsed headers as key value object + */ +function parseHeaders(headers) { + var parsed = {}, key, val, i; + + if (!headers) return parsed; + + forEach(headers.split('\n'), function(line) { + i = line.indexOf(':'); + key = lowercase(trim(line.substr(0, i))); + val = trim(line.substr(i + 1)); + + if (key) { + if (parsed[key]) { + parsed[key] += ', ' + val; + } else { + parsed[key] = val; + } + } + }); + + return parsed; +} + + +var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/; + + +/** + * Parse a request and location URL and determine whether this is a same-domain request. + * + * @param {string} requestUrl The url of the request. + * @param {string} locationUrl The current browser location url. + * @returns {boolean} Whether the request is for the same domain. + */ +function isSameDomain(requestUrl, locationUrl) { + var match = IS_SAME_DOMAIN_URL_MATCH.exec(requestUrl); + // if requestUrl is relative, the regex does not match. + if (match == null) return true; + + var domain1 = { + protocol: match[2], + host: match[4], + port: int(match[6]) || DEFAULT_PORTS[match[2]] || null, + // IE8 sets unmatched groups to '' instead of undefined. + relativeProtocol: match[2] === undefined || match[2] === '' + }; + + match = SERVER_MATCH.exec(locationUrl); + var domain2 = { + protocol: match[1], + host: match[3], + port: int(match[5]) || DEFAULT_PORTS[match[1]] || null + }; + + return (domain1.protocol == domain2.protocol || domain1.relativeProtocol) && + domain1.host == domain2.host && + (domain1.port == domain2.port || (domain1.relativeProtocol && + domain2.port == DEFAULT_PORTS[domain2.protocol])); +} + + +/** + * Returns a function that provides access to parsed headers. + * + * Headers are lazy parsed when first requested. + * @see parseHeaders + * + * @param {(string|Object)} headers Headers to provide access to. + * @returns {function(string=)} Returns a getter function which if called with: + * + * - if called with single an argument returns a single header value or null + * - if called with no arguments returns an object containing all headers. + */ +function headersGetter(headers) { + var headersObj = isObject(headers) ? headers : undefined; + + return function(name) { + if (!headersObj) headersObj = parseHeaders(headers); + + if (name) { + return headersObj[lowercase(name)] || null; + } + + return headersObj; + }; +} + + +/** + * Chain all given functions + * + * This function is used for both request and response transforming + * + * @param {*} data Data to transform. + * @param {function(string=)} headers Http headers getter fn. + * @param {(function|Array.)} fns Function or an array of functions. + * @returns {*} Transformed data. + */ +function transformData(data, headers, fns) { + if (isFunction(fns)) + return fns(data, headers); + + forEach(fns, function(fn) { + data = fn(data, headers); + }); + + return data; +} + + +function isSuccess(status) { + return 200 <= status && status < 300; +} + + +function $HttpProvider() { + var JSON_START = /^\s*(\[|\{[^\{])/, + JSON_END = /[\}\]]\s*$/, + PROTECTION_PREFIX = /^\)\]\}',?\n/, + CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'}; + + var defaults = this.defaults = { + // transform incoming response data + transformResponse: [function(data) { + if (isString(data)) { + // strip json vulnerability protection prefix + data = data.replace(PROTECTION_PREFIX, ''); + if (JSON_START.test(data) && JSON_END.test(data)) + data = fromJson(data, true); + } + return data; + }], + + // transform outgoing request data + transformRequest: [function(d) { + return isObject(d) && !isFile(d) ? toJson(d) : d; + }], + + // default headers + headers: { + common: { + 'Accept': 'application/json, text/plain, */*' + }, + post: CONTENT_TYPE_APPLICATION_JSON, + put: CONTENT_TYPE_APPLICATION_JSON, + patch: CONTENT_TYPE_APPLICATION_JSON + }, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN' + }; + + /** + * Are order by request. I.E. they are applied in the same order as + * array on request, but revers order on response. + */ + var interceptorFactories = this.interceptors = []; + /** + * For historical reasons, response interceptors ordered by the order in which + * they are applied to response. (This is in revers to interceptorFactories) + */ + var responseInterceptorFactories = this.responseInterceptors = []; + + this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', + function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { + + var defaultCache = $cacheFactory('$http'); + + /** + * Interceptors stored in reverse order. Inner interceptors before outer interceptors. + * The reversal is needed so that we can build up the interception chain around the + * server request. + */ + var reversedInterceptors = []; + + forEach(interceptorFactories, function(interceptorFactory) { + reversedInterceptors.unshift(isString(interceptorFactory) + ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); + }); + + forEach(responseInterceptorFactories, function(interceptorFactory, index) { + var responseFn = isString(interceptorFactory) + ? $injector.get(interceptorFactory) + : $injector.invoke(interceptorFactory); + + /** + * Response interceptors go before "around" interceptors (no real reason, just + * had to pick one.) But they are already revesed, so we can't use unshift, hence + * the splice. + */ + reversedInterceptors.splice(index, 0, { + response: function(response) { + return responseFn($q.when(response)); + }, + responseError: function(response) { + return responseFn($q.reject(response)); + } + }); + }); + + + /** + * @ngdoc function + * @name ng.$http + * @requires $httpBackend + * @requires $browser + * @requires $cacheFactory + * @requires $rootScope + * @requires $q + * @requires $injector + * + * @description + * The `$http` service is a core Angular service that facilitates communication with the remote + * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest + * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}. + * + * For unit testing applications that use `$http` service, see + * {@link ngMock.$httpBackend $httpBackend mock}. + * + * For a higher level of abstraction, please check out the {@link ngResource.$resource + * $resource} service. + * + * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by + * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage + * it is important to familiarize yourself with these APIs and the guarantees they provide. + * + * + * # General usage + * The `$http` service is a function which takes a single argument — a configuration object — + * that is used to generate an HTTP request and returns a {@link ng.$q promise} + * with two $http specific methods: `success` and `error`. + * + *
+     *   $http({method: 'GET', url: '/someUrl'}).
+     *     success(function(data, status, headers, config) {
+     *       // this callback will be called asynchronously
+     *       // when the response is available
+     *     }).
+     *     error(function(data, status, headers, config) {
+     *       // called asynchronously if an error occurs
+     *       // or server returns response with an error status.
+     *     });
+     * 
+ * + * Since the returned value of calling the $http function is a `promise`, you can also use + * the `then` method to register callbacks, and these callbacks will receive a single argument – + * an object representing the response. See the API signature and type info below for more + * details. + * + * A response status code between 200 and 299 is considered a success status and + * will result in the success callback being called. Note that if the response is a redirect, + * XMLHttpRequest will transparently follow it, meaning that the error callback will not be + * called for such responses. + * + * # Shortcut methods + * + * Since all invocations of the $http service require passing in an HTTP method and URL, and + * POST/PUT requests require request data to be provided as well, shortcut methods + * were created: + * + *
+     *   $http.get('/someUrl').success(successCallback);
+     *   $http.post('/someUrl', data).success(successCallback);
+     * 
+ * + * Complete list of shortcut methods: + * + * - {@link ng.$http#get $http.get} + * - {@link ng.$http#head $http.head} + * - {@link ng.$http#post $http.post} + * - {@link ng.$http#put $http.put} + * - {@link ng.$http#delete $http.delete} + * - {@link ng.$http#jsonp $http.jsonp} + * + * + * # Setting HTTP Headers + * + * The $http service will automatically add certain HTTP headers to all requests. These defaults + * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration + * object, which currently contains this default configuration: + * + * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): + * - `Accept: application/json, text/plain, * / *` + * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) + * - `Content-Type: application/json` + * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) + * - `Content-Type: application/json` + * + * To add or overwrite these defaults, simply add or remove a property from these configuration + * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object + * with the lowercased HTTP method name as the key, e.g. + * `$httpProvider.defaults.headers.get['My-Header']='value'`. + * + * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same + * fashion. + * + * + * # Transforming Requests and Responses + * + * Both requests and responses can be transformed using transform functions. By default, Angular + * applies these transformations: + * + * Request transformations: + * + * - If the `data` property of the request configuration object contains an object, serialize it into + * JSON format. + * + * Response transformations: + * + * - If XSRF prefix is detected, strip it (see Security Considerations section below). + * - If JSON response is detected, deserialize it using a JSON parser. + * + * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and + * `$httpProvider.defaults.transformResponse` properties. These properties are by default an + * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the + * transformation chain. You can also decide to completely override any default transformations by assigning your + * transformation functions to these properties directly without the array wrapper. + * + * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or + * `transformResponse` properties of the configuration object passed into `$http`. + * + * + * # Caching + * + * To enable caching, set the configuration property `cache` to `true`. When the cache is + * enabled, `$http` stores the response from the server in local cache. Next time the + * response is served from the cache without sending a request to the server. + * + * Note that even if the response is served from cache, delivery of the data is asynchronous in + * the same way that real requests are. + * + * If there are multiple GET requests for the same URL that should be cached using the same + * cache, but the cache is not populated yet, only one request to the server will be made and + * the remaining requests will be fulfilled using the response from the first request. + * + * A custom default cache built with $cacheFactory can be provided in $http.defaults.cache. + * To skip it, set configuration property `cache` to `false`. + * + * + * # Interceptors + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication, or any kind of synchronous or + * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be + * able to intercept requests before they are handed to the server and + * responses before they are handed over to the application code that + * initiated these requests. The interceptors leverage the {@link ng.$q + * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. + * + * The interceptors are service factories that are registered with the `$httpProvider` by + * adding them to the `$httpProvider.interceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor. + * + * There are two kinds of interceptors (and two kinds of rejection interceptors): + * + * * `request`: interceptors get called with http `config` object. The function is free to modify + * the `config` or create a new one. The function needs to return the `config` directly or as a + * promise. + * * `requestError`: interceptor gets called when a previous interceptor threw an error or resolved + * with a rejection. + * * `response`: interceptors get called with http `response` object. The function is free to modify + * the `response` or create a new one. The function needs to return the `response` directly or as a + * promise. + * * `responseError`: interceptor gets called when a previous interceptor threw an error or resolved + * with a rejection. + * + * + *
+     *   // register the interceptor as a service
+     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
+     *     return {
+     *       // optional method
+     *       'request': function(config) {
+     *         // do something on success
+     *         return config || $q.when(config);
+     *       },
+     *
+     *       // optional method
+     *      'requestError': function(rejection) {
+     *         // do something on error
+     *         if (canRecover(rejection)) {
+     *           return responseOrNewPromise
+     *         }
+     *         return $q.reject(rejection);
+     *       },
+     *
+     *
+     *
+     *       // optional method
+     *       'response': function(response) {
+     *         // do something on success
+     *         return response || $q.when(response);
+     *       },
+     *
+     *       // optional method
+     *      'responseError': function(rejection) {
+     *         // do something on error
+     *         if (canRecover(rejection)) {
+     *           return responseOrNewPromise
+     *         }
+     *         return $q.reject(rejection);
+     *       };
+     *     }
+     *   });
+     *
+     *   $httpProvider.interceptors.push('myHttpInterceptor');
+     *
+     *
+     *   // register the interceptor via an anonymous factory
+     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
+     *     return {
+     *      'request': function(config) {
+     *          // same as above
+     *       },
+     *       'response': function(response) {
+     *          // same as above
+     *       }
+     *   });
+     * 
+ * + * # Response interceptors (DEPRECATED) + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication or any kind of synchronous or + * asynchronous preprocessing of received responses, it is desirable to be able to intercept + * responses for http requests before they are handed over to the application code that + * initiated these requests. The response interceptors leverage the {@link ng.$q + * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. + * + * The interceptors are service factories that are registered with the $httpProvider by + * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor — a function that + * takes a {@link ng.$q promise} and returns the original or a new promise. + * + *
+     *   // register the interceptor as a service
+     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
+     *     return function(promise) {
+     *       return promise.then(function(response) {
+     *         // do something on success
+     *       }, function(response) {
+     *         // do something on error
+     *         if (canRecover(response)) {
+     *           return responseOrNewPromise
+     *         }
+     *         return $q.reject(response);
+     *       });
+     *     }
+     *   });
+     *
+     *   $httpProvider.responseInterceptors.push('myHttpInterceptor');
+     *
+     *
+     *   // register the interceptor via an anonymous factory
+     *   $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) {
+     *     return function(promise) {
+     *       // same as above
+     *     }
+     *   });
+     * 
+ * + * + * # Security Considerations + * + * When designing web applications, consider security threats from: + * + * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx + * JSON vulnerability} + * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} + * + * Both server and the client must cooperate in order to eliminate these threats. Angular comes + * pre-configured with strategies that address these issues, but for this to work backend server + * cooperation is required. + * + * ## JSON Vulnerability Protection + * + * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx + * JSON vulnerability} allows third party website to turn your JSON resource URL into + * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To + * counter this your server can prefix all JSON requests with following string `")]}',\n"`. + * Angular will automatically strip the prefix before processing it as JSON. + * + * For example if your server needs to return: + *
+     * ['one','two']
+     * 
+ * + * which is vulnerable to attack, your server can return: + *
+     * )]}',
+     * ['one','two']
+     * 
+ * + * Angular will strip the prefix, before processing the JSON. + * + * + * ## Cross Site Request Forgery (XSRF) Protection + * + * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which + * an unauthorized site can gain your user's private data. Angular provides a mechanism + * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie + * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only + * JavaScript that runs on your domain could read the cookie, your server can be assured that + * the XHR came from JavaScript running on your domain. The header will not be set for + * cross-domain requests. + * + * To take advantage of this, your server needs to set a token in a JavaScript readable session + * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the + * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure + * that only JavaScript running on your domain could have sent the request. The token must be + * unique for each user and must be verifiable by the server (to prevent the JavaScript from making + * up its own tokens). We recommend that the token is a digest of your site's authentication + * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security. + * + * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName + * properties of either $httpProvider.defaults, or the per-request config object. + * + * + * @param {object} config Object describing the request to be made and how it should be + * processed. The object has following properties: + * + * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) + * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. + * - **params** – `{Object.}` – Map of strings or objects which will be turned to + * `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified. + * - **data** – `{string|Object}` – Data to be sent as the request message data. + * - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server. + * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. + * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. + * - **transformRequest** – `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * request body and headers and returns its transformed (typically serialized) version. + * - **transformResponse** – `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * response body and headers and returns its transformed (typically deserialized) version. + * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the + * GET request, otherwise if a cache instance built with + * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for + * caching. + * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} + * that should abort the request when resolved. + * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the + * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5 + * requests with credentials} for more information. + * - **responseType** - `{string}` - see {@link + * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}. + * + * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the + * standard `then` method and two http specific methods: `success` and `error`. The `then` + * method takes two arguments a success and an error callback which will be called with a + * response object. The `success` and `error` methods take a single argument - a function that + * will be called when the request succeeds or fails respectively. The arguments passed into + * these functions are destructured representation of the response object passed into the + * `then` method. The response object has these properties: + * + * - **data** – `{string|Object}` – The response body transformed with the transform functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * + * @property {Array.} pendingRequests Array of config objects for currently pending + * requests. This is primarily meant to be used for debugging purposes. + * + * + * @example + + +
+ + +
+ + + +
http status code: {{status}}
+
http response data: {{data}}
+
+
+ + function FetchCtrl($scope, $http, $templateCache) { + $scope.method = 'GET'; + $scope.url = 'http-hello.html'; + + $scope.fetch = function() { + $scope.code = null; + $scope.response = null; + + $http({method: $scope.method, url: $scope.url, cache: $templateCache}). + success(function(data, status) { + $scope.status = status; + $scope.data = data; + }). + error(function(data, status) { + $scope.data = data || "Request failed"; + $scope.status = status; + }); + }; + + $scope.updateModel = function(method, url) { + $scope.method = method; + $scope.url = url; + }; + } + + + Hello, $http! + + + it('should make an xhr GET request', function() { + element(':button:contains("Sample GET")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('200'); + expect(binding('data')).toMatch(/Hello, \$http!/); + }); + + it('should make a JSONP request to angularjs.org', function() { + element(':button:contains("Sample JSONP")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('200'); + expect(binding('data')).toMatch(/Super Hero!/); + }); + + it('should make JSONP request to invalid URL and invoke the error handler', + function() { + element(':button:contains("Invalid JSONP")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('0'); + expect(binding('data')).toBe('Request failed'); + }); + +
+ */ + function $http(requestConfig) { + var config = { + transformRequest: defaults.transformRequest, + transformResponse: defaults.transformResponse + }; + var headers = {}; + + extend(config, requestConfig); + config.headers = headers; + config.method = uppercase(config.method); + + extend(headers, + defaults.headers.common, + defaults.headers[lowercase(config.method)], + requestConfig.headers); + + var xsrfValue = isSameDomain(config.url, $browser.url()) + ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] + : undefined; + if (xsrfValue) { + headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; + } + + + var serverRequest = function(config) { + var reqData = transformData(config.data, headersGetter(headers), config.transformRequest); + + // strip content-type if data is undefined + if (isUndefined(config.data)) { + delete headers['Content-Type']; + } + + if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { + config.withCredentials = defaults.withCredentials; + } + + // send request + return sendReq(config, reqData, headers).then(transformResponse, transformResponse); + }; + + var chain = [serverRequest, undefined]; + var promise = $q.when(config); + + // apply interceptors + forEach(reversedInterceptors, function(interceptor) { + if (interceptor.request || interceptor.requestError) { + chain.unshift(interceptor.request, interceptor.requestError); + } + if (interceptor.response || interceptor.responseError) { + chain.push(interceptor.response, interceptor.responseError); + } + }); + + while(chain.length) { + var thenFn = chain.shift(); + var rejectFn = chain.shift(); + + promise = promise.then(thenFn, rejectFn); + } + + promise.success = function(fn) { + promise.then(function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + promise.error = function(fn) { + promise.then(null, function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + return promise; + + function transformResponse(response) { + // make a copy since the response must be cacheable + var resp = extend({}, response, { + data: transformData(response.data, response.headers, config.transformResponse) + }); + return (isSuccess(response.status)) + ? resp + : $q.reject(resp); + } + } + + $http.pendingRequests = []; + + /** + * @ngdoc method + * @name ng.$http#get + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `GET` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#delete + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `DELETE` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#head + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `HEAD` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#jsonp + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `JSONP` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request. + * Should contain `JSON_CALLBACK` string. + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethods('get', 'delete', 'head', 'jsonp'); + + /** + * @ngdoc method + * @name ng.$http#post + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `POST` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#put + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `PUT` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethodsWithData('post', 'put'); + + /** + * @ngdoc property + * @name ng.$http#defaults + * @propertyOf ng.$http + * + * @description + * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of + * default headers, withCredentials as well as request and response transformations. + * + * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. + */ + $http.defaults = defaults; + + + return $http; + + + function createShortMethods(names) { + forEach(arguments, function(name) { + $http[name] = function(url, config) { + return $http(extend(config || {}, { + method: name, + url: url + })); + }; + }); + } + + + function createShortMethodsWithData(name) { + forEach(arguments, function(name) { + $http[name] = function(url, data, config) { + return $http(extend(config || {}, { + method: name, + url: url, + data: data + })); + }; + }); + } + + + /** + * Makes the request. + * + * !!! ACCESSES CLOSURE VARS: + * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests + */ + function sendReq(config, reqData, reqHeaders) { + var deferred = $q.defer(), + promise = deferred.promise, + cache, + cachedResp, + url = buildUrl(config.url, config.params); + + $http.pendingRequests.push(config); + promise.then(removePendingReq, removePendingReq); + + + if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') { + cache = isObject(config.cache) ? config.cache + : isObject(defaults.cache) ? defaults.cache + : defaultCache; + } + + if (cache) { + cachedResp = cache.get(url); + if (cachedResp) { + if (cachedResp.then) { + // cached request has already been sent, but there is no response yet + cachedResp.then(removePendingReq, removePendingReq); + return cachedResp; + } else { + // serving from cache + if (isArray(cachedResp)) { + resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2])); + } else { + resolvePromise(cachedResp, 200, {}); + } + } + } else { + // put the promise for the non-transformed response into cache as a placeholder + cache.put(url, promise); + } + } + + // if we won't have the response in cache, send the request to the backend + if (!cachedResp) { + $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, + config.withCredentials, config.responseType); + } + + return promise; + + + /** + * Callback registered to $httpBackend(): + * - caches the response if desired + * - resolves the raw $http promise + * - calls $apply + */ + function done(status, response, headersString) { + if (cache) { + if (isSuccess(status)) { + cache.put(url, [status, response, parseHeaders(headersString)]); + } else { + // remove promise from the cache + cache.remove(url); + } + } + + resolvePromise(response, status, headersString); + if (!$rootScope.$$phase) $rootScope.$apply(); + } + + + /** + * Resolves the raw $http promise. + */ + function resolvePromise(response, status, headers) { + // normalize internal statuses to 0 + status = Math.max(status, 0); + + (isSuccess(status) ? deferred.resolve : deferred.reject)({ + data: response, + status: status, + headers: headersGetter(headers), + config: config + }); + } + + + function removePendingReq() { + var idx = indexOf($http.pendingRequests, config); + if (idx !== -1) $http.pendingRequests.splice(idx, 1); + } + } + + + function buildUrl(url, params) { + if (!params) return url; + var parts = []; + forEachSorted(params, function(value, key) { + if (value == null || value == undefined) return; + if (!isArray(value)) value = [value]; + + forEach(value, function(v) { + if (isObject(v)) { + v = toJson(v); + } + parts.push(encodeUriQuery(key) + '=' + + encodeUriQuery(v)); + }); + }); + return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); + } + + + }]; +} + +var XHR = window.XMLHttpRequest || function() { + try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} + try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} + try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} + throw new Error("This browser does not support XMLHttpRequest."); +}; + + +/** + * @ngdoc object + * @name ng.$httpBackend + * @requires $browser + * @requires $window + * @requires $document + * + * @description + * HTTP backend used by the {@link ng.$http service} that delegates to + * XMLHttpRequest object or JSONP and deals with browser incompatibilities. + * + * You should never need to use this service directly, instead use the higher-level abstractions: + * {@link ng.$http $http} or {@link ngResource.$resource $resource}. + * + * During testing this implementation is swapped with {@link ngMock.$httpBackend mock + * $httpBackend} which can be trained with responses. + */ +function $HttpBackendProvider() { + this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { + return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, + $document[0], $window.location.protocol.replace(':', '')); + }]; +} + +function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) { + // TODO(vojta): fix the signature + return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { + var status; + $browser.$$incOutstandingRequestCount(); + url = url || $browser.url(); + + if (lowercase(method) == 'jsonp') { + var callbackId = '_' + (callbacks.counter++).toString(36); + callbacks[callbackId] = function(data) { + callbacks[callbackId].data = data; + }; + + var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), + function() { + if (callbacks[callbackId].data) { + completeRequest(callback, 200, callbacks[callbackId].data); + } else { + completeRequest(callback, status || -2); + } + delete callbacks[callbackId]; + }); + } else { + var xhr = new XHR(); + xhr.open(method, url, true); + forEach(headers, function(value, key) { + if (value) xhr.setRequestHeader(key, value); + }); + + // In IE6 and 7, this might be called synchronously when xhr.send below is called and the + // response is in the cache. the promise api will ensure that to the app code the api is + // always async + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + var responseHeaders = xhr.getAllResponseHeaders(); + + // TODO(vojta): remove once Firefox 21 gets released. + // begin: workaround to overcome Firefox CORS http response headers bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=608735 + // Firefox already patched in nightly. Should land in Firefox 21. + + // CORS "simple response headers" http://www.w3.org/TR/cors/ + var value, + simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type", + "Expires", "Last-Modified", "Pragma"]; + if (!responseHeaders) { + responseHeaders = ""; + forEach(simpleHeaders, function (header) { + var value = xhr.getResponseHeader(header); + if (value) { + responseHeaders += header + ": " + value + "\n"; + } + }); + } + // end of the workaround. + + // responseText is the old-school way of retrieving response (supported by IE8 & 9) + // response and responseType properties were introduced in XHR Level2 spec (supported by IE10) + completeRequest(callback, + status || xhr.status, + (xhr.responseType ? xhr.response : xhr.responseText), + responseHeaders); + } + }; + + if (withCredentials) { + xhr.withCredentials = true; + } + + if (responseType) { + xhr.responseType = responseType; + } + + xhr.send(post || ''); + } + + if (timeout > 0) { + var timeoutId = $browserDefer(timeoutRequest, timeout); + } else if (timeout && timeout.then) { + timeout.then(timeoutRequest); + } + + + function timeoutRequest() { + status = -1; + jsonpDone && jsonpDone(); + xhr && xhr.abort(); + } + + function completeRequest(callback, status, response, headersString) { + // URL_MATCH is defined in src/service/location.js + var protocol = (url.match(SERVER_MATCH) || ['', locationProtocol])[1]; + + // cancel timeout and subsequent timeout promise resolution + timeoutId && $browserDefer.cancel(timeoutId); + jsonpDone = xhr = null; + + // fix status code for file protocol (it's always 0) + status = (protocol == 'file') ? (response ? 200 : 404) : status; + + // normalize IE bug (http://bugs.jquery.com/ticket/1450) + status = status == 1223 ? 204 : status; + + callback(status, response, headersString); + $browser.$$completeOutstandingRequest(noop); + } + }; + + function jsonpReq(url, done) { + // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: + // - fetches local scripts via XHR and evals them + // - adds and immediately removes script elements from the document + var script = rawDocument.createElement('script'), + doneWrapper = function() { + rawDocument.body.removeChild(script); + if (done) done(); + }; + + script.type = 'text/javascript'; + script.src = url; + + if (msie) { + script.onreadystatechange = function() { + if (/loaded|complete/.test(script.readyState)) doneWrapper(); + }; + } else { + script.onload = script.onerror = doneWrapper; + } + + rawDocument.body.appendChild(script); + return doneWrapper; + } +} + +/** + * @ngdoc object + * @name ng.$locale + * + * @description + * $locale service provides localization rules for various Angular components. As of right now the + * only public api is: + * + * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) + */ +function $LocaleProvider(){ + this.$get = function() { + return { + id: 'en-us', + + NUMBER_FORMATS: { + DECIMAL_SEP: '.', + GROUP_SEP: ',', + PATTERNS: [ + { // Decimal Pattern + minInt: 1, + minFrac: 0, + maxFrac: 3, + posPre: '', + posSuf: '', + negPre: '-', + negSuf: '', + gSize: 3, + lgSize: 3 + },{ //Currency Pattern + minInt: 1, + minFrac: 2, + maxFrac: 2, + posPre: '\u00A4', + posSuf: '', + negPre: '(\u00A4', + negSuf: ')', + gSize: 3, + lgSize: 3 + } + ], + CURRENCY_SYM: '$' + }, + + DATETIME_FORMATS: { + MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December' + .split(','), + SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), + DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), + SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), + AMPMS: ['AM','PM'], + medium: 'MMM d, y h:mm:ss a', + short: 'M/d/yy h:mm a', + fullDate: 'EEEE, MMMM d, y', + longDate: 'MMMM d, y', + mediumDate: 'MMM d, y', + shortDate: 'M/d/yy', + mediumTime: 'h:mm:ss a', + shortTime: 'h:mm a' + }, + + pluralCat: function(num) { + if (num === 1) { + return 'one'; + } + return 'other'; + } + }; + }; +} + +function $TimeoutProvider() { + this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', + function($rootScope, $browser, $q, $exceptionHandler) { + var deferreds = {}; + + + /** + * @ngdoc function + * @name ng.$timeout + * @requires $browser + * + * @description + * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch + * block and delegates any exceptions to + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * The return value of registering a timeout function is a promise, which will be resolved when + * the timeout is reached and the timeout function is executed. + * + * To cancel a timeout request, call `$timeout.cancel(promise)`. + * + * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to + * synchronously flush the queue of deferred functions. + * + * @param {function()} fn A function, whose execution should be delayed. + * @param {number=} [delay=0] Delay in milliseconds. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this + * promise will be resolved with is the return value of the `fn` function. + */ + function timeout(fn, delay, invokeApply) { + var deferred = $q.defer(), + promise = deferred.promise, + skipApply = (isDefined(invokeApply) && !invokeApply), + timeoutId, cleanup; + + timeoutId = $browser.defer(function() { + try { + deferred.resolve(fn()); + } catch(e) { + deferred.reject(e); + $exceptionHandler(e); + } + + if (!skipApply) $rootScope.$apply(); + }, delay); + + cleanup = function() { + delete deferreds[promise.$$timeoutId]; + }; + + promise.$$timeoutId = timeoutId; + deferreds[timeoutId] = deferred; + promise.then(cleanup, cleanup); + + return promise; + } + + + /** + * @ngdoc function + * @name ng.$timeout#cancel + * @methodOf ng.$timeout + * + * @description + * Cancels a task associated with the `promise`. As a result of this, the promise will be + * resolved with a rejection. + * + * @param {Promise=} promise Promise returned by the `$timeout` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + timeout.cancel = function(promise) { + if (promise && promise.$$timeoutId in deferreds) { + deferreds[promise.$$timeoutId].reject('canceled'); + return $browser.defer.cancel(promise.$$timeoutId); + } + return false; + }; + + return timeout; + }]; +} + +/** + * @ngdoc object + * @name ng.$filterProvider + * @description + * + * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To + * achieve this a filter definition consists of a factory function which is annotated with dependencies and is + * responsible for creating a filter function. + * + *
+ *   // Filter registration
+ *   function MyModule($provide, $filterProvider) {
+ *     // create a service to demonstrate injection (not always needed)
+ *     $provide.value('greet', function(name){
+ *       return 'Hello ' + name + '!';
+ *     });
+ *
+ *     // register a filter factory which uses the
+ *     // greet service to demonstrate DI.
+ *     $filterProvider.register('greet', function(greet){
+ *       // return the filter function which uses the greet service
+ *       // to generate salutation
+ *       return function(text) {
+ *         // filters need to be forgiving so check input validity
+ *         return text && greet(text) || text;
+ *       };
+ *     });
+ *   }
+ * 
+ * + * The filter function is registered with the `$injector` under the filter name suffixe with `Filter`. + *
+ *   it('should be the same instance', inject(
+ *     function($filterProvider) {
+ *       $filterProvider.register('reverse', function(){
+ *         return ...;
+ *       });
+ *     },
+ *     function($filter, reverseFilter) {
+ *       expect($filter('reverse')).toBe(reverseFilter);
+ *     });
+ * 
+ * + * + * For more information about how angular filters work, and how to create your own filters, see + * {@link guide/dev_guide.templates.filters Understanding Angular Filters} in the angular Developer + * Guide. + */ +/** + * @ngdoc method + * @name ng.$filterProvider#register + * @methodOf ng.$filterProvider + * @description + * Register filter factory function. + * + * @param {String} name Name of the filter. + * @param {function} fn The filter factory function which is injectable. + */ + + +/** + * @ngdoc function + * @name ng.$filter + * @function + * @description + * Filters are used for formatting data displayed to the user. + * + * The general syntax in templates is as follows: + * + * {{ expression [| filter_name[:parameter_value] ... ] }} + * + * @param {String} name Name of the filter function to retrieve + * @return {Function} the filter function + */ +$FilterProvider.$inject = ['$provide']; +function $FilterProvider($provide) { + var suffix = 'Filter'; + + function register(name, factory) { + return $provide.factory(name + suffix, factory); + } + this.register = register; + + this.$get = ['$injector', function($injector) { + return function(name) { + return $injector.get(name + suffix); + } + }]; + + //////////////////////////////////////// + + register('currency', currencyFilter); + register('date', dateFilter); + register('filter', filterFilter); + register('json', jsonFilter); + register('limitTo', limitToFilter); + register('lowercase', lowercaseFilter); + register('number', numberFilter); + register('orderBy', orderByFilter); + register('uppercase', uppercaseFilter); +} + +/** + * @ngdoc filter + * @name ng.filter:filter + * @function + * + * @description + * Selects a subset of items from `array` and returns it as a new array. + * + * Note: This function is used to augment the `Array` type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @param {Array} array The source array. + * @param {string|Object|function()} expression The predicate to be used for selecting items from + * `array`. + * + * Can be one of: + * + * - `string`: Predicate that results in a substring match using the value of `expression` + * string. All strings or objects with string properties in `array` that contain this string + * will be returned. The predicate can be negated by prefixing the string with `!`. + * + * - `Object`: A pattern object can be used to filter specific properties on objects contained + * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items + * which have property `name` containing "M" and property `phone` containing "1". A special + * property name `$` can be used (as in `{$:"text"}`) to accept a match against any + * property of the object. That's equivalent to the simple substring match with a `string` + * as described above. + * + * - `function`: A predicate function can be used to write arbitrary filters. The function is + * called for each element of `array`. The final result is an array of those elements that + * the predicate returned true for. + * + * @param {function(expected, actual)|true|undefined} comparator Comparator which is used in + * determining if the expected value (from the filter expression) and actual value (from + * the object in the array) should be considered a match. + * + * Can be one of: + * + * - `function(expected, actual)`: + * The function will be given the object value and the predicate value to compare and + * should return true if the item should be included in filtered result. + * + * - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`. + * this is essentially strict comparison of expected and actual. + * + * - `false|undefined`: A short hand for a function which will look for a substring match in case + * insensitive way. + * + * @example + + +
+ + Search: + + + + + + +
NamePhone
{{friend.name}}{{friend.phone}}
+
+ Any:
+ Name only
+ Phone only
+ Equality
+ + + + + + +
NamePhone
{{friend.name}}{{friend.phone}}
+
+ + it('should search across all fields when filtering with a string', function() { + input('searchText').enter('m'); + expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Mike', 'Adam']); + + input('searchText').enter('76'); + expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). + toEqual(['John', 'Julie']); + }); + + it('should search in specific fields when filtering with a predicate object', function() { + input('search.$').enter('i'); + expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Mike', 'Julie', 'Juliette']); + }); + it('should use a equal comparison when comparator is true', function() { + input('search.name').enter('Julie'); + input('strict').check(); + expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). + toEqual(['Julie']); + }); + +
+ */ +function filterFilter() { + return function(array, expression, comperator) { + if (!isArray(array)) return array; + var predicates = []; + predicates.check = function(value) { + for (var j = 0; j < predicates.length; j++) { + if(!predicates[j](value)) { + return false; + } + } + return true; + }; + switch(typeof comperator) { + case "function": + break; + case "boolean": + if(comperator == true) { + comperator = function(obj, text) { + return angular.equals(obj, text); + } + break; + } + default: + comperator = function(obj, text) { + text = (''+text).toLowerCase(); + return (''+obj).toLowerCase().indexOf(text) > -1 + }; + } + var search = function(obj, text){ + if (typeof text == 'string' && text.charAt(0) === '!') { + return !search(obj, text.substr(1)); + } + switch (typeof obj) { + case "boolean": + case "number": + case "string": + return comperator(obj, text); + case "object": + switch (typeof text) { + case "object": + return comperator(obj, text); + break; + default: + for ( var objKey in obj) { + if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { + return true; + } + } + break; + } + return false; + case "array": + for ( var i = 0; i < obj.length; i++) { + if (search(obj[i], text)) { + return true; + } + } + return false; + default: + return false; + } + }; + switch (typeof expression) { + case "boolean": + case "number": + case "string": + expression = {$:expression}; + case "object": + for (var key in expression) { + if (key == '$') { + (function() { + if (!expression[key]) return; + var path = key + predicates.push(function(value) { + return search(value, expression[path]); + }); + })(); + } else { + (function() { + if (!expression[key]) return; + var path = key; + predicates.push(function(value) { + return search(getter(value,path), expression[path]); + }); + })(); + } + } + break; + case 'function': + predicates.push(expression); + break; + default: + return array; + } + var filtered = []; + for ( var j = 0; j < array.length; j++) { + var value = array[j]; + if (predicates.check(value)) { + filtered.push(value); + } + } + return filtered; + } +} + +/** + * @ngdoc filter + * @name ng.filter:currency + * @function + * + * @description + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. + * + * @param {number} amount Input to filter. + * @param {string=} symbol Currency symbol or identifier to be displayed. + * @returns {string} Formatted number. + * + * + * @example + + + +
+
+ default currency symbol ($): {{amount | currency}}
+ custom currency identifier (USD$): {{amount | currency:"USD$"}} +
+
+ + it('should init with 1234.56', function() { + expect(binding('amount | currency')).toBe('$1,234.56'); + expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56'); + }); + it('should update', function() { + input('amount').enter('-1234'); + expect(binding('amount | currency')).toBe('($1,234.00)'); + expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)'); + }); + +
+ */ +currencyFilter.$inject = ['$locale']; +function currencyFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(amount, currencySymbol){ + if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; + return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). + replace(/\u00A4/g, currencySymbol); + }; +} + +/** + * @ngdoc filter + * @name ng.filter:number + * @function + * + * @description + * Formats a number as text. + * + * If the input is not a number an empty string is returned. + * + * @param {number|string} number Number to format. + * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to. + * @returns {string} Number rounded to decimalPlaces and places a “,†after each third digit. + * + * @example + + + +
+ Enter number:
+ Default formatting: {{val | number}}
+ No fractions: {{val | number:0}}
+ Negative number: {{-val | number:4}} +
+
+ + it('should format numbers', function() { + expect(binding('val | number')).toBe('1,234.568'); + expect(binding('val | number:0')).toBe('1,235'); + expect(binding('-val | number:4')).toBe('-1,234.5679'); + }); + + it('should update', function() { + input('val').enter('3374.333'); + expect(binding('val | number')).toBe('3,374.333'); + expect(binding('val | number:0')).toBe('3,374'); + expect(binding('-val | number:4')).toBe('-3,374.3330'); + }); + +
+ */ + + +numberFilter.$inject = ['$locale']; +function numberFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(number, fractionSize) { + return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, + fractionSize); + }; +} + +var DECIMAL_SEP = '.'; +function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { + if (isNaN(number) || !isFinite(number)) return ''; + + var isNegative = number < 0; + number = Math.abs(number); + var numStr = number + '', + formatedText = '', + parts = []; + + var hasExponent = false; + if (numStr.indexOf('e') !== -1) { + var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); + if (match && match[2] == '-' && match[3] > fractionSize + 1) { + numStr = '0'; + } else { + formatedText = numStr; + hasExponent = true; + } + } + + if (!hasExponent) { + var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; + + // determine fractionSize if it is not specified + if (isUndefined(fractionSize)) { + fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); + } + + var pow = Math.pow(10, fractionSize); + number = Math.round(number * pow) / pow; + var fraction = ('' + number).split(DECIMAL_SEP); + var whole = fraction[0]; + fraction = fraction[1] || ''; + + var pos = 0, + lgroup = pattern.lgSize, + group = pattern.gSize; + + if (whole.length >= (lgroup + group)) { + pos = whole.length - lgroup; + for (var i = 0; i < pos; i++) { + if ((pos - i)%group === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + } + + for (i = pos; i < whole.length; i++) { + if ((whole.length - i)%lgroup === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + + // format fraction part. + while(fraction.length < fractionSize) { + fraction += '0'; + } + + if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); + } + + parts.push(isNegative ? pattern.negPre : pattern.posPre); + parts.push(formatedText); + parts.push(isNegative ? pattern.negSuf : pattern.posSuf); + return parts.join(''); +} + +function padNumber(num, digits, trim) { + var neg = ''; + if (num < 0) { + neg = '-'; + num = -num; + } + num = '' + num; + while(num.length < digits) num = '0' + num; + if (trim) + num = num.substr(num.length - digits); + return neg + num; +} + + +function dateGetter(name, size, offset, trim) { + offset = offset || 0; + return function(date) { + var value = date['get' + name](); + if (offset > 0 || value > -offset) + value += offset; + if (value === 0 && offset == -12 ) value = 12; + return padNumber(value, size, trim); + }; +} + +function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date['get' + name](); + var get = uppercase(shortForm ? ('SHORT' + name) : name); + + return formats[get][value]; + }; +} + +function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset(); + var paddedZone = (zone >= 0) ? "+" : ""; + + paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + + padNumber(Math.abs(zone % 60), 2); + + return paddedZone; +} + +function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; +} + +var DATE_FORMATS = { + yyyy: dateGetter('FullYear', 4), + yy: dateGetter('FullYear', 2, 0, true), + y: dateGetter('FullYear', 1), + MMMM: dateStrGetter('Month'), + MMM: dateStrGetter('Month', true), + MM: dateGetter('Month', 2, 1), + M: dateGetter('Month', 1, 1), + dd: dateGetter('Date', 2), + d: dateGetter('Date', 1), + HH: dateGetter('Hours', 2), + H: dateGetter('Hours', 1), + hh: dateGetter('Hours', 2, -12), + h: dateGetter('Hours', 1, -12), + mm: dateGetter('Minutes', 2), + m: dateGetter('Minutes', 1), + ss: dateGetter('Seconds', 2), + s: dateGetter('Seconds', 1), + // while ISO 8601 requires fractions to be prefixed with `.` or `,` + // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions + sss: dateGetter('Milliseconds', 3), + EEEE: dateStrGetter('Day'), + EEE: dateStrGetter('Day', true), + a: ampmGetter, + Z: timeZoneGetter +}; + +var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, + NUMBER_STRING = /^\d+$/; + +/** + * @ngdoc filter + * @name ng.filter:date + * @function + * + * @description + * Formats `date` to a string based on the requested `format`. + * + * `format` string can be composed of the following elements: + * + * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + * * `'MMMM'`: Month in year (January-December) + * * `'MMM'`: Month in year (Jan-Dec) + * * `'MM'`: Month in year, padded (01-12) + * * `'M'`: Month in year (1-12) + * * `'dd'`: Day in month, padded (01-31) + * * `'d'`: Day in month (1-31) + * * `'EEEE'`: Day in Week,(Sunday-Saturday) + * * `'EEE'`: Day in Week, (Sun-Sat) + * * `'HH'`: Hour in day, padded (00-23) + * * `'H'`: Hour in day (0-23) + * * `'hh'`: Hour in am/pm, padded (01-12) + * * `'h'`: Hour in am/pm, (1-12) + * * `'mm'`: Minute in hour, padded (00-59) + * * `'m'`: Minute in hour (0-59) + * * `'ss'`: Second in minute, padded (00-59) + * * `'s'`: Second in minute (0-59) + * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) + * * `'a'`: am/pm marker + * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) + * + * `format` string can also be one of the following predefined + * {@link guide/i18n localizable formats}: + * + * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale + * (e.g. Sep 3, 2010 12:05:08 pm) + * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm) + * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale + * (e.g. Friday, September 3, 2010) + * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010 + * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) + * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) + * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm) + * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm) + * + * `format` string can contain literal values. These need to be quoted with single quotes (e.g. + * `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence + * (e.g. `"h o''clock"`). + * + * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or + * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its + * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is + * specified in the string input, the time is considered to be in the local timezone. + * @param {string=} format Formatting rules (see Description). If not specified, + * `mediumDate` is used. + * @returns {string} Formatted string or the input if input is not recognized as date/millis. + * + * @example + + + {{1288323623006 | date:'medium'}}: + {{1288323623006 | date:'medium'}}
+ {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
+ {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: + {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
+
+ + it('should format date', function() { + expect(binding("1288323623006 | date:'medium'")). + toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); + expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")). + toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); + expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")). + toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); + }); + +
+ */ +dateFilter.$inject = ['$locale']; +function dateFilter($locale) { + + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + // 1 2 3 4 5 6 7 8 9 10 11 + function jsonStringToDate(string) { + var match; + if (match = string.match(R_ISO8601_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0, + dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, + timeSetter = match[8] ? date.setUTCHours : date.setHours; + + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); + var h = int(match[4]||0) - tzHour; + var m = int(match[5]||0) - tzMin + var s = int(match[6]||0); + var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + return string; + } + + + return function(date, format) { + var text = '', + parts = [], + fn, match; + + format = format || 'mediumDate'; + format = $locale.DATETIME_FORMATS[format] || format; + if (isString(date)) { + if (NUMBER_STRING.test(date)) { + date = int(date); + } else { + date = jsonStringToDate(date); + } + } + + if (isNumber(date)) { + date = new Date(date); + } + + if (!isDate(date)) { + return date; + } + + while(format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + forEach(parts, function(value){ + fn = DATE_FORMATS[value]; + text += fn ? fn(date, $locale.DATETIME_FORMATS) + : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); + }); + + return text; + }; +} + + +/** + * @ngdoc filter + * @name ng.filter:json + * @function + * + * @description + * Allows you to convert a JavaScript object into JSON string. + * + * This filter is mostly useful for debugging. When using the double curly {{value}} notation + * the binding is automatically converted to JSON. + * + * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. + * @returns {string} JSON string. + * + * + * @example: + + +
{{ {'name':'value'} | json }}
+
+ + it('should jsonify filtered objects', function() { + expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/); + }); + +
+ * + */ +function jsonFilter() { + return function(object) { + return toJson(object, true); + }; +} + + +/** + * @ngdoc filter + * @name ng.filter:lowercase + * @function + * @description + * Converts string to lowercase. + * @see angular.lowercase + */ +var lowercaseFilter = valueFn(lowercase); + + +/** + * @ngdoc filter + * @name ng.filter:uppercase + * @function + * @description + * Converts string to uppercase. + * @see angular.uppercase + */ +var uppercaseFilter = valueFn(uppercase); + +/** + * @ngdoc function + * @name ng.filter:limitTo + * @function + * + * @description + * Creates a new array or string containing only a specified number of elements. The elements + * are taken from either the beginning or the end of the source array or string, as specified by + * the value and sign (positive or negative) of `limit`. + * + * Note: This function is used to augment the `Array` type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @param {Array|string} input Source array or string to be limited. + * @param {string|number} limit The length of the returned array or string. If the `limit` number + * is positive, `limit` number of items from the beginning of the source array/string are copied. + * If the number is negative, `limit` number of items from the end of the source array/string + * are copied. The `limit` will be trimmed if it exceeds `array.length` + * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array + * had less than `limit` elements. + * + * @example + + + +
+ Limit {{numbers}} to: +

Output numbers: {{ numbers | limitTo:numLimit }}

+ Limit {{letters}} to: +

Output letters: {{ letters | limitTo:letterLimit }}

+
+
+ + it('should limit the number array to first three items', function() { + expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3'); + expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3'); + expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]'); + expect(binding('letters | limitTo:letterLimit')).toEqual('abc'); + }); + + it('should update the output when -3 is entered', function() { + input('numLimit').enter(-3); + input('letterLimit').enter(-3); + expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]'); + expect(binding('letters | limitTo:letterLimit')).toEqual('ghi'); + }); + + it('should not exceed the maximum size of input array', function() { + input('numLimit').enter(100); + input('letterLimit').enter(100); + expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]'); + expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi'); + }); + +
+ */ +function limitToFilter(){ + return function(input, limit) { + if (!isArray(input) && !isString(input)) return input; + + limit = int(limit); + + if (isString(input)) { + //NaN check on limit + if (limit) { + return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length); + } else { + return ""; + } + } + + var out = [], + i, n; + + // if abs(limit) exceeds maximum length, trim it + if (limit > input.length) + limit = input.length; + else if (limit < -input.length) + limit = -input.length; + + if (limit > 0) { + i = 0; + n = limit; + } else { + i = input.length + limit; + n = input.length; + } + + for (; i} expression A predicate to be + * used by the comparator to determine the order of elements. + * + * Can be one of: + * + * - `function`: Getter function. The result of this function will be sorted using the + * `<`, `=`, `>` operator. + * - `string`: An Angular expression which evaluates to an object to order by, such as 'name' + * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control + * ascending or descending sort order (for example, +name or -name). + * - `Array`: An array of function or string predicates. The first predicate in the array + * is used for sorting, but when two items are equivalent, the next predicate is used. + * + * @param {boolean=} reverse Reverse the order the array. + * @returns {Array} Sorted copy of the source array. + * + * @example + + + +
+
Sorting predicate = {{predicate}}; reverse = {{reverse}}
+
+ [ unsorted ] + + + + + + + + + + + +
Name + (^)Phone NumberAge
{{friend.name}}{{friend.phone}}{{friend.age}}
+
+
+ + it('should be reverse ordered by aged', function() { + expect(binding('predicate')).toBe('-age'); + expect(repeater('table.friend', 'friend in friends').column('friend.age')). + toEqual(['35', '29', '21', '19', '10']); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']); + }); + + it('should reorder the table when user selects different predicate', function() { + element('.doc-example-live a:contains("Name")').click(); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']); + expect(repeater('table.friend', 'friend in friends').column('friend.age')). + toEqual(['35', '10', '29', '19', '21']); + + element('.doc-example-live a:contains("Phone")').click(); + expect(repeater('table.friend', 'friend in friends').column('friend.phone')). + toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']); + }); + +
+ */ +orderByFilter.$inject = ['$parse']; +function orderByFilter($parse){ + return function(array, sortPredicate, reverseOrder) { + if (!isArray(array)) return array; + if (!sortPredicate) return array; + sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; + sortPredicate = map(sortPredicate, function(predicate){ + var descending = false, get = predicate || identity; + if (isString(predicate)) { + if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { + descending = predicate.charAt(0) == '-'; + predicate = predicate.substring(1); + } + get = $parse(predicate); + } + return reverseComparator(function(a,b){ + return compare(get(a),get(b)); + }, descending); + }); + var arrayCopy = []; + for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } + return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); + + function comparator(o1, o2){ + for ( var i = 0; i < sortPredicate.length; i++) { + var comp = sortPredicate[i](o1, o2); + if (comp !== 0) return comp; + } + return 0; + } + function reverseComparator(comp, descending) { + return toBoolean(descending) + ? function(a,b){return comp(b,a);} + : comp; + } + function compare(v1, v2){ + var t1 = typeof v1; + var t2 = typeof v2; + if (t1 == t2) { + if (t1 == "string") v1 = v1.toLowerCase(); + if (t1 == "string") v2 = v2.toLowerCase(); + if (v1 === v2) return 0; + return v1 < v2 ? -1 : 1; + } else { + return t1 < t2 ? -1 : 1; + } + } + } +} + +function ngDirective(directive) { + if (isFunction(directive)) { + directive = { + link: directive + } + } + directive.restrict = directive.restrict || 'AC'; + return valueFn(directive); +} + +/** + * @ngdoc directive + * @name ng.directive:a + * @restrict E + * + * @description + * Modifies the default behavior of html A tag, so that the default action is prevented when href + * attribute is empty. + * + * The reasoning for this change is to allow easy creation of action links with `ngClick` directive + * without changing the location or causing page reloads, e.g.: + * `Save` + */ +var htmlAnchorDirective = valueFn({ + restrict: 'E', + compile: function(element, attr) { + + if (msie <= 8) { + + // turn link into a stylable link in IE + // but only if it doesn't have name attribute, in which case it's an anchor + if (!attr.href && !attr.name) { + attr.$set('href', ''); + } + + // add a comment node to anchors to workaround IE bug that causes element content to be reset + // to new attribute content if attribute is updated with value containing @ and element also + // contains value with @ + // see issue #1949 + element.append(document.createComment('IE fix')); + } + + return function(scope, element) { + element.bind('click', function(event){ + // if we have no href url, then don't navigate anywhere. + if (!element.attr('href')) { + event.preventDefault(); + } + }); + } + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngHref + * @restrict A + * + * @description + * Using Angular markup like {{hash}} in an href attribute makes + * the page open to a wrong URL, if the user clicks that link before + * angular has a chance to replace the {{hash}} with actual URL, the + * link will be broken and will most likely return a 404 error. + * The `ngHref` directive solves this problem. + * + * The buggy way to write it: + *
+ * 
+ * 
+ * + * The correct way to write it: + *
+ * 
+ * 
+ * + * @element A + * @param {template} ngHref any string which can contain `{{}}` markup. + * + * @example + * This example uses `link` variable inside `href` attribute: + + +
+
link 1 (link, don't reload)
+ link 2 (link, don't reload)
+ link 3 (link, reload!)
+ anchor (link, don't reload)
+ anchor (no link)
+ link (link, change location) + + + it('should execute ng-click but not reload when href without value', function() { + element('#link-1').click(); + expect(input('value').val()).toEqual('1'); + expect(element('#link-1').attr('href')).toBe(""); + }); + + it('should execute ng-click but not reload when href empty string', function() { + element('#link-2').click(); + expect(input('value').val()).toEqual('2'); + expect(element('#link-2').attr('href')).toBe(""); + }); + + it('should execute ng-click and change url when ng-href specified', function() { + expect(element('#link-3').attr('href')).toBe("/123"); + + element('#link-3').click(); + expect(browser().window().path()).toEqual('/123'); + }); + + it('should execute ng-click but not reload when href empty string and name specified', function() { + element('#link-4').click(); + expect(input('value').val()).toEqual('4'); + expect(element('#link-4').attr('href')).toBe(''); + }); + + it('should execute ng-click but not reload when no href but name specified', function() { + element('#link-5').click(); + expect(input('value').val()).toEqual('5'); + expect(element('#link-5').attr('href')).toBe(undefined); + }); + + it('should only change url when only ng-href', function() { + input('value').enter('6'); + expect(element('#link-6').attr('href')).toBe('6'); + + element('#link-6').click(); + expect(browser().location().url()).toEqual('/6'); + }); + + + */ + +/** + * @ngdoc directive + * @name ng.directive:ngSrc + * @restrict A + * + * @description + * Using Angular markup like `{{hash}}` in a `src` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrc` directive solves this problem. + * + * The buggy way to write it: + *
+ * 
+ * 
+ * + * The correct way to write it: + *
+ * 
+ * 
+ * + * @element IMG + * @param {template} ngSrc any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ng.directive:ngSrcset + * @restrict A + * + * @description + * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrcset` directive solves this problem. + * + * The buggy way to write it: + *
+ * 
+ * 
+ * + * The correct way to write it: + *
+ * 
+ * 
+ * + * @element IMG + * @param {template} ngSrcset any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ng.directive:ngDisabled + * @restrict A + * + * @description + * + * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: + *
+ * 
+ * + *
+ *
+ * + * The HTML specs do not require browsers to preserve the special attributes such as disabled. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngDisabled` directive. + * + * @example + + + Click me to toggle:
+ +
+ + it('should toggle button', function() { + expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy(); + input('checked').check(); + expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy(); + }); + +
+ * + * @element INPUT + * @param {expression} ngDisabled Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngChecked + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as checked. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngChecked` directive. + * @example + + + Check me to check both:
+ +
+ + it('should check both checkBoxes', function() { + expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy(); + input('master').check(); + expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy(); + }); + +
+ * + * @element INPUT + * @param {expression} ngChecked Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMultiple + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as multiple. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngMultiple` directive. + * + * @example + + + Check me check multiple:
+ +
+ + it('should toggle multiple', function() { + expect(element('.doc-example-live #select').prop('multiple')).toBeFalsy(); + input('checked').check(); + expect(element('.doc-example-live #select').prop('multiple')).toBeTruthy(); + }); + +
+ * + * @element SELECT + * @param {expression} ngMultiple Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngReadonly + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as readonly. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngReadonly` directive. + * @example + + + Check me to make text readonly:
+ +
+ + it('should toggle readonly attr', function() { + expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy(); + input('checked').check(); + expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy(); + }); + +
+ * + * @element INPUT + * @param {string} expression Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngSelected + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as selected. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduced the `ngSelected` directive. + * @example + + + Check me to select:
+ +
+ + it('should select Greetings!', function() { + expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy(); + input('selected').check(); + expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy(); + }); + +
+ * + * @element OPTION + * @param {string} expression Angular expression that will be evaluated. + */ + +/** + * @ngdoc directive + * @name ng.directive:ngOpen + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as open. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngOpen` directive. + * + * @example + + + Check me check multiple:
+
+ Show/Hide me +
+
+ + it('should toggle open', function() { + expect(element('#details').prop('open')).toBeFalsy(); + input('open').check(); + expect(element('#details').prop('open')).toBeTruthy(); + }); + +
+ * + * @element DETAILS + * @param {string} expression Angular expression that will be evaluated. + */ + +var ngAttributeAliasDirectives = {}; + + +// boolean attrs are evaluated +forEach(BOOLEAN_ATTR, function(propName, attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 100, + compile: function() { + return function(scope, element, attr) { + scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { + attr.$set(attrName, !!value); + }); + }; + } + }; + }; +}); + + +// ng-src, ng-srcset, ng-href are interpolated +forEach(['src', 'srcset', 'href'], function(attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 99, // it needs to run after the attributes are interpolated + link: function(scope, element, attr) { + attr.$observe(normalized, function(value) { + if (!value) + return; + + attr.$set(attrName, value); + + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect. + // we use attr[attrName] value since $set can sanitize the url. + if (msie) element.prop(attrName, attr[attrName]); + }); + } + }; + }; +}); + +var nullFormCtrl = { + $addControl: noop, + $removeControl: noop, + $setValidity: noop, + $setDirty: noop, + $setPristine: noop +}; + +/** + * @ngdoc object + * @name ng.directive:form.FormController + * + * @property {boolean} $pristine True if user has not interacted with the form yet. + * @property {boolean} $dirty True if user has already interacted with the form. + * @property {boolean} $valid True if all of the containing forms and controls are valid. + * @property {boolean} $invalid True if at least one containing control or form is invalid. + * + * @property {Object} $error Is an object hash, containing references to all invalid controls or + * forms, where: + * + * - keys are validation tokens (error names) — such as `required`, `url` or `email`), + * - values are arrays of controls or forms that are invalid with given error. + * + * @description + * `FormController` keeps track of all its controls and nested forms as well as state of them, + * such as being valid/invalid or dirty/pristine. + * + * Each {@link ng.directive:form form} directive creates an instance + * of `FormController`. + * + */ +//asks for $scope to fool the BC controller module +FormController.$inject = ['$element', '$attrs', '$scope']; +function FormController(element, attrs) { + var form = this, + parentForm = element.parent().controller('form') || nullFormCtrl, + invalidCount = 0, // used to easily determine if we are valid + errors = form.$error = {}, + controls = []; + + // init state + form.$name = attrs.name; + form.$dirty = false; + form.$pristine = true; + form.$valid = true; + form.$invalid = false; + + parentForm.$addControl(form); + + // Setup initial state of the control + element.addClass(PRISTINE_CLASS); + toggleValidCss(true); + + // convenience method for easy toggling of classes + function toggleValidCss(isValid, validationErrorKey) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + element. + removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). + addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); + } + + form.$addControl = function(control) { + controls.push(control); + + if (control.$name && !form.hasOwnProperty(control.$name)) { + form[control.$name] = control; + } + }; + + form.$removeControl = function(control) { + if (control.$name && form[control.$name] === control) { + delete form[control.$name]; + } + forEach(errors, function(queue, validationToken) { + form.$setValidity(validationToken, true, control); + }); + + arrayRemove(controls, control); + }; + + form.$setValidity = function(validationToken, isValid, control) { + var queue = errors[validationToken]; + + if (isValid) { + if (queue) { + arrayRemove(queue, control); + if (!queue.length) { + invalidCount--; + if (!invalidCount) { + toggleValidCss(isValid); + form.$valid = true; + form.$invalid = false; + } + errors[validationToken] = false; + toggleValidCss(true, validationToken); + parentForm.$setValidity(validationToken, true, form); + } + } + + } else { + if (!invalidCount) { + toggleValidCss(isValid); + } + if (queue) { + if (includes(queue, control)) return; + } else { + errors[validationToken] = queue = []; + invalidCount++; + toggleValidCss(false, validationToken); + parentForm.$setValidity(validationToken, false, form); + } + queue.push(control); + + form.$valid = false; + form.$invalid = true; + } + }; + + form.$setDirty = function() { + element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); + form.$dirty = true; + form.$pristine = false; + parentForm.$setDirty(); + }; + + /** + * @ngdoc function + * @name ng.directive:form.FormController#$setPristine + * @methodOf ng.directive:form.FormController + * + * @description + * Sets the form to its pristine state. + * + * This method can be called to remove the 'ng-dirty' class and set the form to its pristine + * state (ng-pristine class). This method will also propagate to all the controls contained + * in this form. + * + * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after + * saving or resetting it. + */ + form.$setPristine = function () { + element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS); + form.$dirty = false; + form.$pristine = true; + forEach(controls, function(control) { + control.$setPristine(); + }); + }; +} + + +/** + * @ngdoc directive + * @name ng.directive:ngForm + * @restrict EAC + * + * @description + * Nestable alias of {@link ng.directive:form `form`} directive. HTML + * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a + * sub-group of controls needs to be determined. + * + * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + */ + + /** + * @ngdoc directive + * @name ng.directive:form + * @restrict E + * + * @description + * Directive that instantiates + * {@link ng.directive:form.FormController FormController}. + * + * If `name` attribute is specified, the form controller is published onto the current scope under + * this name. + * + * # Alias: {@link ng.directive:ngForm `ngForm`} + * + * In angular forms can be nested. This means that the outer form is valid when all of the child + * forms are valid as well. However browsers do not allow nesting of `
` elements, for this + * reason angular provides {@link ng.directive:ngForm `ngForm`} alias + * which behaves identical to `` but allows form nesting. + * + * + * # CSS classes + * - `ng-valid` Is set if the form is valid. + * - `ng-invalid` Is set if the form is invalid. + * - `ng-pristine` Is set if the form is pristine. + * - `ng-dirty` Is set if the form is dirty. + * + * + * # Submitting a form and preventing default action + * + * Since the role of forms in client-side Angular applications is different than in classical + * roundtrip apps, it is desirable for the browser not to translate the form submission into a full + * page reload that sends the data to the server. Instead some javascript logic should be triggered + * to handle the form submission in application specific way. + * + * For this reason, Angular prevents the default action (form submission to the server) unless the + * `` element has an `action` attribute specified. + * + * You can use one of the following two ways to specify what javascript method should be called when + * a form is submitted: + * + * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element + * - {@link ng.directive:ngClick ngClick} directive on the first + * button or input field of type submit (input[type=submit]) + * + * To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This + * is because of the following form submission rules coming from the html spec: + * + * - If a form has only one input field then hitting enter in this field triggers form submit + * (`ngSubmit`) + * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter + * doesn't trigger submit + * - if a form has one or more input fields and one or more buttons or input[type=submit] then + * hitting enter in any of the input fields will trigger the click handler on the *first* button or + * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) + * + * @param {string=} name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + * @example + + + + + userType: + Required!
+ userType = {{userType}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ +
+ + it('should initialize to model', function() { + expect(binding('userType')).toEqual('guest'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('userType').enter(''); + expect(binding('userType')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
+ */ +var formDirectiveFactory = function(isNgForm) { + return ['$timeout', function($timeout) { + var formDirective = { + name: 'form', + restrict: 'E', + controller: FormController, + compile: function() { + return { + pre: function(scope, formElement, attr, controller) { + if (!attr.action) { + // we can't use jq events because if a form is destroyed during submission the default + // action is not prevented. see #1238 + // + // IE 9 is not affected because it doesn't fire a submit event and try to do a full + // page reload if the form was destroyed by submission of the form via a click handler + // on a button in the form. Looks like an IE9 specific bug. + var preventDefaultListener = function(event) { + event.preventDefault + ? event.preventDefault() + : event.returnValue = false; // IE + }; + + addEventListenerFn(formElement[0], 'submit', preventDefaultListener); + + // unregister the preventDefault listener so that we don't not leak memory but in a + // way that will achieve the prevention of the default action. + formElement.bind('$destroy', function() { + $timeout(function() { + removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); + }, 0, false); + }); + } + + var parentFormCtrl = formElement.parent().controller('form'), + alias = attr.name || attr.ngForm; + + if (alias) { + scope[alias] = controller; + } + if (parentFormCtrl) { + formElement.bind('$destroy', function() { + parentFormCtrl.$removeControl(controller); + if (alias) { + scope[alias] = undefined; + } + extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards + }); + } + } + }; + } + }; + + return isNgForm ? extend(copy(formDirective), {restrict: 'EAC'}) : formDirective; + }]; +}; + +var formDirective = formDirectiveFactory(); +var ngFormDirective = formDirectiveFactory(true); + +var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; +var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; +var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; + +var inputType = { + + /** + * @ngdoc inputType + * @name ng.directive:input.text + * + * @description + * Standard HTML text input with angular data binding. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the + * input. + * + * @example + + + +
+ Single word: + + Required! + + Single word only! + + text = {{text}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+
+
+ + it('should initialize to model', function() { + expect(binding('text')).toEqual('guest'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('text').enter(''); + expect(binding('text')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if multi word', function() { + input('text').enter('hello world'); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should not be trimmed', function() { + input('text').enter('untrimmed '); + expect(binding('text')).toEqual('untrimmed '); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + +
+ */ + 'text': textInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.number + * + * @description + * Text input with number validation and transformation. Sets the `number` validation + * error if not a valid number. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ Number: + + Required! + + Not valid number! + value = {{value}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+
+
+ + it('should initialize to model', function() { + expect(binding('value')).toEqual('12'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('value').enter(''); + expect(binding('value')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if over max', function() { + input('value').enter('123'); + expect(binding('value')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
+ */ + 'number': numberInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.url + * + * @description + * Text input with URL validation. Sets the `url` validation error key if the content is not a + * valid URL. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ URL: + + Required! + + Not valid url! + text = {{text}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ myForm.$error.url = {{!!myForm.$error.url}}
+
+
+ + it('should initialize to model', function() { + expect(binding('text')).toEqual('http://google.com'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('text').enter(''); + expect(binding('text')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if not url', function() { + input('text').enter('xxx'); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
+ */ + 'url': urlInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.email + * + * @description + * Text input with email validation. Sets the `email` validation error key if not a valid email + * address. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * + * @example + + + +
+ Email: + + Required! + + Not valid email! + text = {{text}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ myForm.$error.email = {{!!myForm.$error.email}}
+
+
+ + it('should initialize to model', function() { + expect(binding('text')).toEqual('me@example.com'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('text').enter(''); + expect(binding('text')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if not email', function() { + input('text').enter('xxx'); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
+ */ + 'email': emailInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.radio + * + * @description + * HTML radio button. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string} value The value to which the expression should be set when selected. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ Red
+ Green
+ Blue
+ color = {{color}}
+
+
+ + it('should change state', function() { + expect(binding('color')).toEqual('blue'); + + input('color').select('red'); + expect(binding('color')).toEqual('red'); + }); + +
+ */ + 'radio': radioInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.checkbox + * + * @description + * HTML checkbox. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngTrueValue The value to which the expression should be set when selected. + * @param {string=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ Value1:
+ Value2:
+ value1 = {{value1}}
+ value2 = {{value2}}
+
+
+ + it('should change state', function() { + expect(binding('value1')).toEqual('true'); + expect(binding('value2')).toEqual('YES'); + + input('value1').check(); + input('value2').check(); + expect(binding('value1')).toEqual('false'); + expect(binding('value2')).toEqual('NO'); + }); + +
+ */ + 'checkbox': checkboxInputType, + + 'hidden': noop, + 'button': noop, + 'submit': noop, + 'reset': noop +}; + + +function isEmpty(value) { + return isUndefined(value) || value === '' || value === null || value !== value; +} + + +function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { + + var listener = function() { + var value = element.val(); + + // By default we will trim the value + // If the attribute ng-trim exists we will avoid trimming + // e.g. + if (toBoolean(attr.ngTrim || 'T')) { + value = trim(value); + } + + if (ctrl.$viewValue !== value) { + scope.$apply(function() { + ctrl.$setViewValue(value); + }); + } + }; + + // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the + // input event on backspace, delete or cut + if ($sniffer.hasEvent('input')) { + element.bind('input', listener); + } else { + var timeout; + + var deferListener = function() { + if (!timeout) { + timeout = $browser.defer(function() { + listener(); + timeout = null; + }); + } + }; + + element.bind('keydown', function(event) { + var key = event.keyCode; + + // ignore + // command modifiers arrows + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; + + deferListener(); + }); + + // if user paste into input using mouse, we need "change" event to catch it + element.bind('change', listener); + + // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it + if ($sniffer.hasEvent('paste')) { + element.bind('paste cut', deferListener); + } + } + + + ctrl.$render = function() { + element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); + }; + + // pattern validator + var pattern = attr.ngPattern, + patternValidator, + match; + + var validate = function(regexp, value) { + if (isEmpty(value) || regexp.test(value)) { + ctrl.$setValidity('pattern', true); + return value; + } else { + ctrl.$setValidity('pattern', false); + return undefined; + } + }; + + if (pattern) { + match = pattern.match(/^\/(.*)\/([gim]*)$/); + if (match) { + pattern = new RegExp(match[1], match[2]); + patternValidator = function(value) { + return validate(pattern, value) + }; + } else { + patternValidator = function(value) { + var patternObj = scope.$eval(pattern); + + if (!patternObj || !patternObj.test) { + throw new Error('Expected ' + pattern + ' to be a RegExp but was ' + patternObj); + } + return validate(patternObj, value); + }; + } + + ctrl.$formatters.push(patternValidator); + ctrl.$parsers.push(patternValidator); + } + + // min length validator + if (attr.ngMinlength) { + var minlength = int(attr.ngMinlength); + var minLengthValidator = function(value) { + if (!isEmpty(value) && value.length < minlength) { + ctrl.$setValidity('minlength', false); + return undefined; + } else { + ctrl.$setValidity('minlength', true); + return value; + } + }; + + ctrl.$parsers.push(minLengthValidator); + ctrl.$formatters.push(minLengthValidator); + } + + // max length validator + if (attr.ngMaxlength) { + var maxlength = int(attr.ngMaxlength); + var maxLengthValidator = function(value) { + if (!isEmpty(value) && value.length > maxlength) { + ctrl.$setValidity('maxlength', false); + return undefined; + } else { + ctrl.$setValidity('maxlength', true); + return value; + } + }; + + ctrl.$parsers.push(maxLengthValidator); + ctrl.$formatters.push(maxLengthValidator); + } +} + +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + ctrl.$parsers.push(function(value) { + var empty = isEmpty(value); + if (empty || NUMBER_REGEXP.test(value)) { + ctrl.$setValidity('number', true); + return value === '' ? null : (empty ? value : parseFloat(value)); + } else { + ctrl.$setValidity('number', false); + return undefined; + } + }); + + ctrl.$formatters.push(function(value) { + return isEmpty(value) ? '' : '' + value; + }); + + if (attr.min) { + var min = parseFloat(attr.min); + var minValidator = function(value) { + if (!isEmpty(value) && value < min) { + ctrl.$setValidity('min', false); + return undefined; + } else { + ctrl.$setValidity('min', true); + return value; + } + }; + + ctrl.$parsers.push(minValidator); + ctrl.$formatters.push(minValidator); + } + + if (attr.max) { + var max = parseFloat(attr.max); + var maxValidator = function(value) { + if (!isEmpty(value) && value > max) { + ctrl.$setValidity('max', false); + return undefined; + } else { + ctrl.$setValidity('max', true); + return value; + } + }; + + ctrl.$parsers.push(maxValidator); + ctrl.$formatters.push(maxValidator); + } + + ctrl.$formatters.push(function(value) { + + if (isEmpty(value) || isNumber(value)) { + ctrl.$setValidity('number', true); + return value; + } else { + ctrl.$setValidity('number', false); + return undefined; + } + }); +} + +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var urlValidator = function(value) { + if (isEmpty(value) || URL_REGEXP.test(value)) { + ctrl.$setValidity('url', true); + return value; + } else { + ctrl.$setValidity('url', false); + return undefined; + } + }; + + ctrl.$formatters.push(urlValidator); + ctrl.$parsers.push(urlValidator); +} + +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var emailValidator = function(value) { + if (isEmpty(value) || EMAIL_REGEXP.test(value)) { + ctrl.$setValidity('email', true); + return value; + } else { + ctrl.$setValidity('email', false); + return undefined; + } + }; + + ctrl.$formatters.push(emailValidator); + ctrl.$parsers.push(emailValidator); +} + +function radioInputType(scope, element, attr, ctrl) { + // make the name unique, if not defined + if (isUndefined(attr.name)) { + element.attr('name', nextUid()); + } + + element.bind('click', function() { + if (element[0].checked) { + scope.$apply(function() { + ctrl.$setViewValue(attr.value); + }); + } + }); + + ctrl.$render = function() { + var value = attr.value; + element[0].checked = (value == ctrl.$viewValue); + }; + + attr.$observe('value', ctrl.$render); +} + +function checkboxInputType(scope, element, attr, ctrl) { + var trueValue = attr.ngTrueValue, + falseValue = attr.ngFalseValue; + + if (!isString(trueValue)) trueValue = true; + if (!isString(falseValue)) falseValue = false; + + element.bind('click', function() { + scope.$apply(function() { + ctrl.$setViewValue(element[0].checked); + }); + }); + + ctrl.$render = function() { + element[0].checked = ctrl.$viewValue; + }; + + ctrl.$formatters.push(function(value) { + return value === trueValue; + }); + + ctrl.$parsers.push(function(value) { + return value ? trueValue : falseValue; + }); +} + + +/** + * @ngdoc directive + * @name ng.directive:textarea + * @restrict E + * + * @description + * HTML textarea element control with angular data-binding. The data-binding and validation + * properties of this element are exactly the same as those of the + * {@link ng.directive:input input element}. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + */ + + +/** + * @ngdoc directive + * @name ng.directive:input + * @restrict E + * + * @description + * HTML input element control with angular data-binding. Input control follows HTML5 input types + * and polyfills the HTML5 validation behavior for older browsers. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {boolean=} ngRequired Sets `required` attribute if set to true + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+
+ User name: + + Required!
+ Last name: + + Too short! + + Too long!
+
+
+ user = {{user}}
+ myForm.userName.$valid = {{myForm.userName.$valid}}
+ myForm.userName.$error = {{myForm.userName.$error}}
+ myForm.lastName.$valid = {{myForm.lastName.$valid}}
+ myForm.lastName.$error = {{myForm.lastName.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ myForm.$error.minlength = {{!!myForm.$error.minlength}}
+ myForm.$error.maxlength = {{!!myForm.$error.maxlength}}
+
+
+ + it('should initialize to model', function() { + expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}'); + expect(binding('myForm.userName.$valid')).toEqual('true'); + expect(binding('myForm.$valid')).toEqual('true'); + }); + + it('should be invalid if empty when required', function() { + input('user.name').enter(''); + expect(binding('user')).toEqual('{"last":"visitor"}'); + expect(binding('myForm.userName.$valid')).toEqual('false'); + expect(binding('myForm.$valid')).toEqual('false'); + }); + + it('should be valid if empty when min length is set', function() { + input('user.last').enter(''); + expect(binding('user')).toEqual('{"name":"guest","last":""}'); + expect(binding('myForm.lastName.$valid')).toEqual('true'); + expect(binding('myForm.$valid')).toEqual('true'); + }); + + it('should be invalid if less than required min length', function() { + input('user.last').enter('xx'); + expect(binding('user')).toEqual('{"name":"guest"}'); + expect(binding('myForm.lastName.$valid')).toEqual('false'); + expect(binding('myForm.lastName.$error')).toMatch(/minlength/); + expect(binding('myForm.$valid')).toEqual('false'); + }); + + it('should be invalid if longer than max length', function() { + input('user.last').enter('some ridiculously long name'); + expect(binding('user')) + .toEqual('{"name":"guest"}'); + expect(binding('myForm.lastName.$valid')).toEqual('false'); + expect(binding('myForm.lastName.$error')).toMatch(/maxlength/); + expect(binding('myForm.$valid')).toEqual('false'); + }); + +
+ */ +var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { + return { + restrict: 'E', + require: '?ngModel', + link: function(scope, element, attr, ctrl) { + if (ctrl) { + (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, + $browser); + } + } + }; +}]; + +var VALID_CLASS = 'ng-valid', + INVALID_CLASS = 'ng-invalid', + PRISTINE_CLASS = 'ng-pristine', + DIRTY_CLASS = 'ng-dirty'; + +/** + * @ngdoc object + * @name ng.directive:ngModel.NgModelController + * + * @property {string} $viewValue Actual string value in the view. + * @property {*} $modelValue The value in the model, that the control is bound to. + * @property {Array.} $parsers Whenever the control reads value from the DOM, it executes + * all of these functions to sanitize / convert the value as well as validate. + * + * @property {Array.} $formatters Whenever the model value changes, it executes all of + * these functions to convert the value as well as validate. + * + * @property {Object} $error An object hash with all errors as keys. + * + * @property {boolean} $pristine True if user has not interacted with the control yet. + * @property {boolean} $dirty True if user has already interacted with the control. + * @property {boolean} $valid True if there is no error. + * @property {boolean} $invalid True if at least one error on the control. + * + * @description + * + * `NgModelController` provides API for the `ng-model` directive. The controller contains + * services for data-binding, validation, CSS update, value formatting and parsing. It + * specifically does not contain any logic which deals with DOM rendering or listening to + * DOM events. The `NgModelController` is meant to be extended by other directives where, the + * directive provides DOM manipulation and the `NgModelController` provides the data-binding. + * + * This example shows how to use `NgModelController` with a custom control to achieve + * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) + * collaborate together to achieve the desired result. + * + * + + [contenteditable] { + border: 1px solid black; + background-color: white; + min-height: 20px; + } + + .ng-invalid { + border: 1px solid red; + } + + + + angular.module('customControl', []). + directive('contenteditable', function() { + return { + restrict: 'A', // only activate on element attribute + require: '?ngModel', // get a hold of NgModelController + link: function(scope, element, attrs, ngModel) { + if(!ngModel) return; // do nothing if no ng-model + + // Specify how UI should be updated + ngModel.$render = function() { + element.html(ngModel.$viewValue || ''); + }; + + // Listen for change events to enable binding + element.bind('blur keyup change', function() { + scope.$apply(read); + }); + read(); // initialize + + // Write data to the model + function read() { + ngModel.$setViewValue(element.html()); + } + } + }; + }); + + +
+
Change me!
+ Required! +
+ +
+
+ + it('should data-bind and become invalid', function() { + var contentEditable = element('[contenteditable]'); + + expect(contentEditable.text()).toEqual('Change me!'); + input('userContent').enter(''); + expect(contentEditable.text()).toEqual(''); + expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/); + }); + + *
+ * + */ +var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', + function($scope, $exceptionHandler, $attr, $element, $parse) { + this.$viewValue = Number.NaN; + this.$modelValue = Number.NaN; + this.$parsers = []; + this.$formatters = []; + this.$viewChangeListeners = []; + this.$pristine = true; + this.$dirty = false; + this.$valid = true; + this.$invalid = false; + this.$name = $attr.name; + + var ngModelGet = $parse($attr.ngModel), + ngModelSet = ngModelGet.assign; + + if (!ngModelSet) { + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + $attr.ngModel + + ' (' + startingTag($element) + ')'); + } + + /** + * @ngdoc function + * @name ng.directive:ngModel.NgModelController#$render + * @methodOf ng.directive:ngModel.NgModelController + * + * @description + * Called when the view needs to be updated. It is expected that the user of the ng-model + * directive will implement this method. + */ + this.$render = noop; + + var parentForm = $element.inheritedData('$formController') || nullFormCtrl, + invalidCount = 0, // used to easily determine if we are valid + $error = this.$error = {}; // keep invalid keys here + + + // Setup initial state of the control + $element.addClass(PRISTINE_CLASS); + toggleValidCss(true); + + // convenience method for easy toggling of classes + function toggleValidCss(isValid, validationErrorKey) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + $element. + removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). + addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); + } + + /** + * @ngdoc function + * @name ng.directive:ngModel.NgModelController#$setValidity + * @methodOf ng.directive:ngModel.NgModelController + * + * @description + * Change the validity state, and notifies the form when the control changes validity. (i.e. it + * does not notify form if given validator is already marked as invalid). + * + * This method should be called by validators - i.e. the parser or formatter functions. + * + * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign + * to `$error[validationErrorKey]=isValid` so that it is available for data-binding. + * The `validationErrorKey` should be in camelCase and will get converted into dash-case + * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` + * class and can be bound to as `{{someForm.someControl.$error.myError}}` . + * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). + */ + this.$setValidity = function(validationErrorKey, isValid) { + if ($error[validationErrorKey] === !isValid) return; + + if (isValid) { + if ($error[validationErrorKey]) invalidCount--; + if (!invalidCount) { + toggleValidCss(true); + this.$valid = true; + this.$invalid = false; + } + } else { + toggleValidCss(false); + this.$invalid = true; + this.$valid = false; + invalidCount++; + } + + $error[validationErrorKey] = !isValid; + toggleValidCss(isValid, validationErrorKey); + + parentForm.$setValidity(validationErrorKey, isValid, this); + }; + + /** + * @ngdoc function + * @name ng.directive:ngModel.NgModelController#$setPristine + * @methodOf ng.directive:ngModel.NgModelController + * + * @description + * Sets the control to its pristine state. + * + * This method can be called to remove the 'ng-dirty' class and set the control to its pristine + * state (ng-pristine class). + */ + this.$setPristine = function () { + this.$dirty = false; + this.$pristine = true; + $element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS); + }; + + /** + * @ngdoc function + * @name ng.directive:ngModel.NgModelController#$setViewValue + * @methodOf ng.directive:ngModel.NgModelController + * + * @description + * Read a value from view. + * + * This method should be called from within a DOM event handler. + * For example {@link ng.directive:input input} or + * {@link ng.directive:select select} directives call it. + * + * It internally calls all `parsers` and if resulted value is valid, updates the model and + * calls all registered change listeners. + * + * @param {string} value Value from the view. + */ + this.$setViewValue = function(value) { + this.$viewValue = value; + + // change to dirty + if (this.$pristine) { + this.$dirty = true; + this.$pristine = false; + $element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); + parentForm.$setDirty(); + } + + forEach(this.$parsers, function(fn) { + value = fn(value); + }); + + if (this.$modelValue !== value) { + this.$modelValue = value; + ngModelSet($scope, value); + forEach(this.$viewChangeListeners, function(listener) { + try { + listener(); + } catch(e) { + $exceptionHandler(e); + } + }) + } + }; + + // model -> value + var ctrl = this; + + $scope.$watch(function ngModelWatch() { + var value = ngModelGet($scope); + + // if scope model value and ngModel value are out of sync + if (ctrl.$modelValue !== value) { + + var formatters = ctrl.$formatters, + idx = formatters.length; + + ctrl.$modelValue = value; + while(idx--) { + value = formatters[idx](value); + } + + if (ctrl.$viewValue !== value) { + ctrl.$viewValue = value; + ctrl.$render(); + } + } + }); +}]; + + +/** + * @ngdoc directive + * @name ng.directive:ngModel + * + * @element input + * + * @description + * Is directive that tells Angular to do two-way data binding. It works together with `input`, + * `select`, `textarea`. You can easily write your own directives to use `ngModel` as well. + * + * `ngModel` is responsible for: + * + * - binding the view into the model, which other directives such as `input`, `textarea` or `select` + * require, + * - providing validation behavior (i.e. required, number, email, url), + * - keeping state of the control (valid/invalid, dirty/pristine, validation errors), + * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`), + * - register the control with parent {@link ng.directive:form form}. + * + * For basic examples, how to use `ngModel`, see: + * + * - {@link ng.directive:input input} + * - {@link ng.directive:input.text text} + * - {@link ng.directive:input.checkbox checkbox} + * - {@link ng.directive:input.radio radio} + * - {@link ng.directive:input.number number} + * - {@link ng.directive:input.email email} + * - {@link ng.directive:input.url url} + * - {@link ng.directive:select select} + * - {@link ng.directive:textarea textarea} + * + */ +var ngModelDirective = function() { + return { + require: ['ngModel', '^?form'], + controller: NgModelController, + link: function(scope, element, attr, ctrls) { + // notify others, especially parent forms + + var modelCtrl = ctrls[0], + formCtrl = ctrls[1] || nullFormCtrl; + + formCtrl.$addControl(modelCtrl); + + element.bind('$destroy', function() { + formCtrl.$removeControl(modelCtrl); + }); + } + }; +}; + + +/** + * @ngdoc directive + * @name ng.directive:ngChange + * @restrict E + * + * @description + * Evaluate given expression when user changes the input. + * The expression is not evaluated when the value change is coming from the model. + * + * Note, this directive requires `ngModel` to be present. + * + * @element input + * + * @example + * + * + * + *
+ * + * + *
+ * debug = {{confirmed}}
+ * counter = {{counter}} + *
+ *
+ * + * it('should evaluate the expression if changing from view', function() { + * expect(binding('counter')).toEqual('0'); + * element('#ng-change-example1').click(); + * expect(binding('counter')).toEqual('1'); + * expect(binding('confirmed')).toEqual('true'); + * }); + * + * it('should not evaluate the expression if changing from model', function() { + * element('#ng-change-example2').click(); + * expect(binding('counter')).toEqual('0'); + * expect(binding('confirmed')).toEqual('true'); + * }); + * + *
+ */ +var ngChangeDirective = valueFn({ + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + ctrl.$viewChangeListeners.push(function() { + scope.$eval(attr.ngChange); + }); + } +}); + + +var requiredDirective = function() { + return { + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + attr.required = true; // force truthy in case we are on non input element + + var validator = function(value) { + if (attr.required && (isEmpty(value) || value === false)) { + ctrl.$setValidity('required', false); + return; + } else { + ctrl.$setValidity('required', true); + return value; + } + }; + + ctrl.$formatters.push(validator); + ctrl.$parsers.unshift(validator); + + attr.$observe('required', function() { + validator(ctrl.$viewValue); + }); + } + }; +}; + + +/** + * @ngdoc directive + * @name ng.directive:ngList + * + * @description + * Text input that converts between comma-separated string into an array of strings. + * + * @element input + * @param {string=} ngList optional delimiter that should be used to split the value. If + * specified in form `/something/` then the value will be converted into a regular expression. + * + * @example + + + +
+ List: + + Required! + names = {{names}}
+ myForm.namesInput.$valid = {{myForm.namesInput.$valid}}
+ myForm.namesInput.$error = {{myForm.namesInput.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+
+
+ + it('should initialize to model', function() { + expect(binding('names')).toEqual('["igor","misko","vojta"]'); + expect(binding('myForm.namesInput.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('names').enter(''); + expect(binding('names')).toEqual('[]'); + expect(binding('myForm.namesInput.$valid')).toEqual('false'); + }); + +
+ */ +var ngListDirective = function() { + return { + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + var match = /\/(.*)\//.exec(attr.ngList), + separator = match && new RegExp(match[1]) || attr.ngList || ','; + + var parse = function(viewValue) { + var list = []; + + if (viewValue) { + forEach(viewValue.split(separator), function(value) { + if (value) list.push(trim(value)); + }); + } + + return list; + }; + + ctrl.$parsers.push(parse); + ctrl.$formatters.push(function(value) { + if (isArray(value)) { + return value.join(', '); + } + + return undefined; + }); + } + }; +}; + + +var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; + +var ngValueDirective = function() { + return { + priority: 100, + compile: function(tpl, tplAttr) { + if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { + return function(scope, elm, attr) { + attr.$set('value', scope.$eval(attr.ngValue)); + }; + } else { + return function(scope, elm, attr) { + scope.$watch(attr.ngValue, function valueWatchAction(value) { + attr.$set('value', value, false); + }); + }; + } + } + }; +}; + +/** + * @ngdoc directive + * @name ng.directive:ngBind + * + * @description + * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element + * with the value of a given expression, and to update the text content when the value of that + * expression changes. + * + * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like + * `{{ expression }}` which is similar but less verbose. + * + * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when + * it's desirable to put bindings into template that is momentarily displayed by the browser in its + * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the + * bindings invisible to the user while the page is loading. + * + * An alternative solution to this problem would be using the + * {@link ng.directive:ngCloak ngCloak} directive. + * + * + * @element ANY + * @param {expression} ngBind {@link guide/expression Expression} to evaluate. + * + * @example + * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. + + + +
+ Enter name:
+ Hello ! +
+
+ + it('should check ng-bind', function() { + expect(using('.doc-example-live').binding('name')).toBe('Whirled'); + using('.doc-example-live').input('name').enter('world'); + expect(using('.doc-example-live').binding('name')).toBe('world'); + }); + +
+ */ +var ngBindDirective = ngDirective(function(scope, element, attr) { + element.addClass('ng-binding').data('$binding', attr.ngBind); + scope.$watch(attr.ngBind, function ngBindWatchAction(value) { + element.text(value == undefined ? '' : value); + }); +}); + + +/** + * @ngdoc directive + * @name ng.directive:ngBindTemplate + * + * @description + * The `ngBindTemplate` directive specifies that the element + * text should be replaced with the template in ngBindTemplate. + * Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}` + * expressions. (This is required since some HTML elements + * can not have SPAN elements such as TITLE, or OPTION to name a few.) + * + * @element ANY + * @param {string} ngBindTemplate template of form + * {{ expression }} to eval. + * + * @example + * Try it here: enter text in text box and watch the greeting change. + + + +
+ Salutation:
+ Name:
+

+       
+
+ + it('should check ng-bind', function() { + expect(using('.doc-example-live').binding('salutation')). + toBe('Hello'); + expect(using('.doc-example-live').binding('name')). + toBe('World'); + using('.doc-example-live').input('salutation').enter('Greetings'); + using('.doc-example-live').input('name').enter('user'); + expect(using('.doc-example-live').binding('salutation')). + toBe('Greetings'); + expect(using('.doc-example-live').binding('name')). + toBe('user'); + }); + +
+ */ +var ngBindTemplateDirective = ['$interpolate', function($interpolate) { + return function(scope, element, attr) { + // TODO: move this to scenario runner + var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); + element.addClass('ng-binding').data('$binding', interpolateFn); + attr.$observe('ngBindTemplate', function(value) { + element.text(value); + }); + } +}]; + + +/** + * @ngdoc directive + * @name ng.directive:ngBindHtmlUnsafe + * + * @description + * Creates a binding that will innerHTML the result of evaluating the `expression` into the current + * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if + * {@link ngSanitize.directive:ngBindHtml ngBindHtml} directive is too + * restrictive and when you absolutely trust the source of the content you are binding to. + * + * See {@link ngSanitize.$sanitize $sanitize} docs for examples. + * + * @element ANY + * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. + */ +var ngBindHtmlUnsafeDirective = [function() { + return function(scope, element, attr) { + element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); + scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) { + element.html(value || ''); + }); + }; +}]; + +function classDirective(name, selector) { + name = 'ngClass' + name; + return ngDirective(function(scope, element, attr) { + var oldVal = undefined; + + scope.$watch(attr[name], ngClassWatchAction, true); + + attr.$observe('class', function(value) { + var ngClass = scope.$eval(attr[name]); + ngClassWatchAction(ngClass, ngClass); + }); + + + if (name !== 'ngClass') { + scope.$watch('$index', function($index, old$index) { + var mod = $index & 1; + if (mod !== old$index & 1) { + if (mod === selector) { + addClass(scope.$eval(attr[name])); + } else { + removeClass(scope.$eval(attr[name])); + } + } + }); + } + + + function ngClassWatchAction(newVal) { + if (selector === true || scope.$index % 2 === selector) { + if (oldVal && !equals(newVal,oldVal)) { + removeClass(oldVal); + } + addClass(newVal); + } + oldVal = copy(newVal); + } + + + function removeClass(classVal) { + if (isObject(classVal) && !isArray(classVal)) { + classVal = map(classVal, function(v, k) { if (v) return k }); + } + element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal); + } + + + function addClass(classVal) { + if (isObject(classVal) && !isArray(classVal)) { + classVal = map(classVal, function(v, k) { if (v) return k }); + } + if (classVal) { + element.addClass(isArray(classVal) ? classVal.join(' ') : classVal); + } + } + }); +} + +/** + * @ngdoc directive + * @name ng.directive:ngClass + * + * @description + * The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an + * expression that represents all classes to be added. + * + * The directive won't add duplicate classes if a particular class was already set. + * + * When the expression changes, the previously added classes are removed and only then the + * new classes are added. + * + * @element ANY + * @param {expression} ngClass {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class + * names, an array, or a map of class names to boolean values. + * + * @example + + + + +
+ Sample Text +
+ + .my-class { + color: red; + } + + + it('should check ng-class', function() { + expect(element('.doc-example-live span').prop('className')).not(). + toMatch(/my-class/); + + using('.doc-example-live').element(':button:first').click(); + + expect(element('.doc-example-live span').prop('className')). + toMatch(/my-class/); + + using('.doc-example-live').element(':button:last').click(); + + expect(element('.doc-example-live span').prop('className')).not(). + toMatch(/my-class/); + }); + +
+ */ +var ngClassDirective = classDirective('', true); + +/** + * @ngdoc directive + * @name ng.directive:ngClassOdd + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except it works in + * conjunction with `ngRepeat` and takes affect only on odd (even) rows. + * + * This directive can be applied only within a scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class names or an array. + * + * @example + + +
    +
  1. + + {{name}} + +
  2. +
+
+ + .odd { + color: red; + } + .even { + color: blue; + } + + + it('should check ng-class-odd and ng-class-even', function() { + expect(element('.doc-example-live li:first span').prop('className')). + toMatch(/odd/); + expect(element('.doc-example-live li:last span').prop('className')). + toMatch(/even/); + }); + +
+ */ +var ngClassOddDirective = classDirective('Odd', 0); + +/** + * @ngdoc directive + * @name ng.directive:ngClassEven + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except it works in + * conjunction with `ngRepeat` and takes affect only on odd (even) rows. + * + * This directive can be applied only within a scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The + * result of the evaluation can be a string representing space delimited class names or an array. + * + * @example + + +
    +
  1. + + {{name}}       + +
  2. +
+
+ + .odd { + color: red; + } + .even { + color: blue; + } + + + it('should check ng-class-odd and ng-class-even', function() { + expect(element('.doc-example-live li:first span').prop('className')). + toMatch(/odd/); + expect(element('.doc-example-live li:last span').prop('className')). + toMatch(/even/); + }); + +
+ */ +var ngClassEvenDirective = classDirective('Even', 1); + +/** + * @ngdoc directive + * @name ng.directive:ngCloak + * + * @description + * The `ngCloak` directive is used to prevent the Angular html template from being briefly + * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this + * directive to avoid the undesirable flicker effect caused by the html template display. + * + * The directive can be applied to the `` element, but typically a fine-grained application is + * preferred in order to benefit from progressive rendering of the browser view. + * + * `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and + * `angular.min.js` files. Following is the css rule: + * + *
+ * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
+ *   display: none;
+ * }
+ * 
+ * + * When this css rule is loaded by the browser, all html elements (including their children) that + * are tagged with the `ng-cloak` directive are hidden. When Angular comes across this directive + * during the compilation of the template it deletes the `ngCloak` element attribute, which + * makes the compiled element visible. + * + * For the best result, `angular.js` script must be loaded in the head section of the html file; + * alternatively, the css rule (above) must be included in the external stylesheet of the + * application. + * + * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they + * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css + * class `ngCloak` in addition to `ngCloak` directive as shown in the example below. + * + * @element ANY + * + * @example + + +
{{ 'hello' }}
+
{{ 'hello IE7' }}
+
+ + it('should remove the template directive and css class', function() { + expect(element('.doc-example-live #template1').attr('ng-cloak')). + not().toBeDefined(); + expect(element('.doc-example-live #template2').attr('ng-cloak')). + not().toBeDefined(); + }); + +
+ * + */ +var ngCloakDirective = ngDirective({ + compile: function(element, attr) { + attr.$set('ngCloak', undefined); + element.removeClass('ng-cloak'); + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngController + * + * @description + * The `ngController` directive assigns behavior to a scope. This is a key aspect of how angular + * supports the principles behind the Model-View-Controller design pattern. + * + * MVC components in angular: + * + * * Model — The Model is data in scope properties; scopes are attached to the DOM. + * * View — The template (HTML with data bindings) is rendered into the View. + * * Controller — The `ngController` directive specifies a Controller class; the class has + * methods that typically express the business logic behind the application. + * + * Note that an alternative way to define controllers is via the {@link ng.$route $route} service. + * + * @element ANY + * @scope + * @param {expression} ngController Name of a globally accessible constructor function or an + * {@link guide/expression expression} that on the current scope evaluates to a + * constructor function. The controller instance can further be published into the scope + * by adding `as localName` the controller name attribute. + * + * @example + * Here is a simple form for editing user contact information. Adding, removing, clearing, and + * greeting are methods declared on the controller (see source tab). These methods can + * easily be called from the angular markup. Notice that the scope becomes the `this` for the + * controller's instance. This allows for easy access to the view data from the controller. Also + * notice that any changes to the data are automatically reflected in the View without the need + * for a manual update. The example is included in two different declaration styles based on + * your style preferences. + + + +
+ Name: + [ greet ]
+ Contact: +
    +
  • + + + [ clear + | X ] +
  • +
  • [ add ]
  • +
+
+
+ + it('should check controller', function() { + expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); + expect(element('.doc-example-live li:nth-child(1) input').val()) + .toBe('408 555 1212'); + expect(element('.doc-example-live li:nth-child(2) input').val()) + .toBe('john.smith@example.org'); + + element('.doc-example-live li:first a:contains("clear")').click(); + expect(element('.doc-example-live li:first input').val()).toBe(''); + + element('.doc-example-live li:last a:contains("add")').click(); + expect(element('.doc-example-live li:nth-child(3) input').val()) + .toBe('yourname@example.org'); + }); + +
+ + + + + + +
+ Name: + [ greet ]
+ Contact: +
    +
  • + + + [ clear + | X ] +
  • +
  • [ add ]
  • +
+
+
+ + it('should check controller', function() { + expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); + expect(element('.doc-example-live li:nth-child(1) input').val()) + .toBe('408 555 1212'); + expect(element('.doc-example-live li:nth-child(2) input').val()) + .toBe('john.smith@example.org'); + + element('.doc-example-live li:first a:contains("clear")').click(); + expect(element('.doc-example-live li:first input').val()).toBe(''); + + element('.doc-example-live li:last a:contains("add")').click(); + expect(element('.doc-example-live li:nth-child(3) input').val()) + .toBe('yourname@example.org'); + }); + +
+ + */ +var ngControllerDirective = [function() { + return { + scope: true, + controller: '@' + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngCsp + * @priority 1000 + * + * @element html + * @description + * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. + * + * This is necessary when developing things like Google Chrome Extensions. + * + * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). + * For us to be compatible, we just need to implement the "getterFn" in $parse without violating + * any of these restrictions. + * + * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp` + * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will + * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will + * be raised. + * + * In order to use this feature put `ngCsp` directive on the root element of the application. + * + * @example + * This example shows how to apply the `ngCsp` directive to the `html` tag. +
+     
+     
+     ...
+     ...
+     
+   
+ */ + +var ngCspDirective = ['$sniffer', function($sniffer) { + return { + priority: 1000, + compile: function() { + $sniffer.csp = true; + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngClick + * + * @description + * The ngClick allows you to specify custom behavior when + * element is clicked. + * + * @element ANY + * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon + * click. (Event object is available as `$event`) + * + * @example + + + + count: {{count}} + + + it('should check ng-click', function() { + expect(binding('count')).toBe('0'); + element('.doc-example-live :button').click(); + expect(binding('count')).toBe('1'); + }); + + + */ +/* + * A directive that allows creation of custom onclick handlers that are defined as angular + * expressions and are compiled and executed within the current scope. + * + * Events that are handled via these handler are always configured not to propagate further. + */ +var ngEventDirectives = {}; +forEach( + 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress'.split(' '), + function(name) { + var directiveName = directiveNormalize('ng-' + name); + ngEventDirectives[directiveName] = ['$parse', function($parse) { + return function(scope, element, attr) { + var fn = $parse(attr[directiveName]); + element.bind(lowercase(name), function(event) { + scope.$apply(function() { + fn(scope, {$event:event}); + }); + }); + }; + }]; + } +); + +/** + * @ngdoc directive + * @name ng.directive:ngDblclick + * + * @description + * The `ngDblclick` directive allows you to specify custom behavior on dblclick event. + * + * @element ANY + * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon + * dblclick. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMousedown + * + * @description + * The ngMousedown directive allows you to specify custom behavior on mousedown event. + * + * @element ANY + * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon + * mousedown. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMouseup + * + * @description + * Specify custom behavior on mouseup event. + * + * @element ANY + * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon + * mouseup. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + +/** + * @ngdoc directive + * @name ng.directive:ngMouseover + * + * @description + * Specify custom behavior on mouseover event. + * + * @element ANY + * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon + * mouseover. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMouseenter + * + * @description + * Specify custom behavior on mouseenter event. + * + * @element ANY + * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon + * mouseenter. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMouseleave + * + * @description + * Specify custom behavior on mouseleave event. + * + * @element ANY + * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon + * mouseleave. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMousemove + * + * @description + * Specify custom behavior on mousemove event. + * + * @element ANY + * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon + * mousemove. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngKeydown + * + * @description + * Specify custom behavior on keydown event. + * + * @element ANY + * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon + * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngKeyup + * + * @description + * Specify custom behavior on keyup event. + * + * @element ANY + * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon + * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngKeypress + * + * @description + * Specify custom behavior on keypress event. + * + * @element ANY + * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon + * keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngSubmit + * + * @description + * Enables binding angular expressions to onsubmit events. + * + * Additionally it prevents the default action (which for form means sending the request to the + * server and reloading the current page). + * + * @element form + * @param {expression} ngSubmit {@link guide/expression Expression} to eval. + * + * @example + + + +
+ Enter text and hit enter: + + +
list={{list}}
+
+
+ + it('should check ng-submit', function() { + expect(binding('list')).toBe('[]'); + element('.doc-example-live #submit').click(); + expect(binding('list')).toBe('["hello"]'); + expect(input('text').val()).toBe(''); + }); + it('should ignore empty strings', function() { + expect(binding('list')).toBe('[]'); + element('.doc-example-live #submit').click(); + element('.doc-example-live #submit').click(); + expect(binding('list')).toBe('["hello"]'); + }); + +
+ */ +var ngSubmitDirective = ngDirective(function(scope, element, attrs) { + element.bind('submit', function() { + scope.$apply(attrs.ngSubmit); + }); +}); + +/** + * @ngdoc directive + * @name ng.directive:ngIf + * @restrict A + * + * @description + * The `ngIf` directive removes and recreates a portion of the DOM tree (HTML) + * conditionally based on **"falsy"** and **"truthy"** values, respectively, evaluated within + * an {expression}. In other words, if the expression assigned to **ngIf evaluates to a false + * value** then **the element is removed from the DOM** and **if true** then **a clone of the + * element is reinserted into the DOM**. + * + * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the + * element in the DOM rather than changing its visibility via the `display` css property. A common + * case when this difference is significant is when using css selectors that rely on an element's + * position within the DOM (HTML), such as the `:first-child` or `:last-child` pseudo-classes. + * + * Note that **when an element is removed using ngIf its scope is destroyed** and **a new scope + * is created when the element is restored**. The scope created within `ngIf` inherits from + * its parent scope using + * {@link https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance}. + * An important implication of this is if `ngModel` is used within `ngIf` to bind to + * a javascript primitive defined in the parent scope. In this case any modifications made to the + * variable within the child scope will override (hide) the value in the parent scope. + * + * Also, `ngIf` recreates elements using their compiled state. An example scenario of this behavior + * is if an element's class attribute is directly modified after it's compiled, using something like + * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element + * the added class will be lost because the original compiled state is used to regenerate the element. + * + * Additionally, you can provide animations via the ngAnimate attribute to animate the **enter** + * and **leave** effects. + * + * @animations + * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container + * leave - happens just before the ngIf contents are removed from the DOM + * + * @element ANY + * @scope + * @param {expression} ngIf If the {@link guide/expression expression} is falsy then + * the element is removed from the DOM tree (HTML). + * + * @example + + + Click me:
+ Show when checked: + + I'm removed when the checkbox is unchecked. + +
+ + .example-leave, .example-enter { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + } + + .example-enter { + opacity:0; + } + .example-enter.example-enter-active { + opacity:1; + } + + .example-leave { + opacity:1; + } + .example-leave.example-leave-active { + opacity:0; + } + +
+ */ +var ngIfDirective = ['$animator', function($animator) { + return { + transclude: 'element', + priority: 1000, + terminal: true, + restrict: 'A', + compile: function (element, attr, transclude) { + return function ($scope, $element, $attr) { + var animate = $animator($scope, $attr); + var childElement, childScope; + $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { + if (childElement) { + animate.leave(childElement); + childElement = undefined; + } + if (childScope) { + childScope.$destroy(); + childScope = undefined; + } + if (toBoolean(value)) { + childScope = $scope.$new(); + transclude(childScope, function (clone) { + childElement = clone; + animate.enter(clone, $element.parent(), $element); + }); + } + }); + } + } + } +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngInclude + * @restrict ECA + * + * @description + * Fetches, compiles and includes an external HTML fragment. + * + * Keep in mind that Same Origin Policy applies to included resources + * (e.g. ngInclude won't work for cross-domain requests on all browsers and for + * file:// access on some browsers). + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** + * and **leave** effects. + * + * @animations + * enter - happens just after the ngInclude contents change and a new DOM element is created and injected into the ngInclude container + * leave - happens just after the ngInclude contents change and just before the former contents are removed from the DOM + * + * @scope + * + * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, + * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`. + * @param {string=} onload Expression to evaluate when a new partial is loaded. + * + * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll + * $anchorScroll} to scroll the viewport after the content is loaded. + * + * - If the attribute is not set, disable scrolling. + * - If the attribute is set without value, enable scrolling. + * - Otherwise enable scrolling only if the expression evaluates to truthy value. + * + * @example + + +
+ + url of the template: {{template.url}} +
+
+
+
+ + function Ctrl($scope) { + $scope.templates = + [ { name: 'template1.html', url: 'template1.html'} + , { name: 'template2.html', url: 'template2.html'} ]; + $scope.template = $scope.templates[0]; + } + + +
Content of template1.html
+
+ +
Content of template2.html
+
+ + .example-leave, + .example-enter { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + } + + .example-animate-container > * { + display:block; + padding:10px; + } + + .example-enter { + top:-50px; + } + .example-enter.example-enter-active { + top:0; + } + + .example-leave { + top:0; + } + .example-leave.example-leave-active { + top:50px; + } + + + it('should load template1.html', function() { + expect(element('.doc-example-live [ng-include]').text()). + toMatch(/Content of template1.html/); + }); + it('should load template2.html', function() { + select('template').option('1'); + expect(element('.doc-example-live [ng-include]').text()). + toMatch(/Content of template2.html/); + }); + it('should change to blank', function() { + select('template').option(''); + expect(element('.doc-example-live [ng-include]').text()).toEqual(''); + }); + +
+ */ + + +/** + * @ngdoc event + * @name ng.directive:ngInclude#$includeContentRequested + * @eventOf ng.directive:ngInclude + * @eventType emit on the scope ngInclude was declared in + * @description + * Emitted every time the ngInclude content is requested. + */ + + +/** + * @ngdoc event + * @name ng.directive:ngInclude#$includeContentLoaded + * @eventOf ng.directive:ngInclude + * @eventType emit on the current ngInclude scope + * @description + * Emitted every time the ngInclude content is reloaded. + */ +var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animator', + function($http, $templateCache, $anchorScroll, $compile, $animator) { + return { + restrict: 'ECA', + terminal: true, + compile: function(element, attr) { + var srcExp = attr.ngInclude || attr.src, + onloadExp = attr.onload || '', + autoScrollExp = attr.autoscroll; + + return function(scope, element, attr) { + var animate = $animator(scope, attr); + var changeCounter = 0, + childScope; + + var clearContent = function() { + if (childScope) { + childScope.$destroy(); + childScope = null; + } + animate.leave(element.contents(), element); + }; + + scope.$watch(srcExp, function ngIncludeWatchAction(src) { + var thisChangeId = ++changeCounter; + + if (src) { + $http.get(src, {cache: $templateCache}).success(function(response) { + if (thisChangeId !== changeCounter) return; + + if (childScope) childScope.$destroy(); + childScope = scope.$new(); + animate.leave(element.contents(), element); + + var contents = jqLite('
').html(response).contents(); + + animate.enter(contents, element); + $compile(contents)(childScope); + + if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + + childScope.$emit('$includeContentLoaded'); + scope.$eval(onloadExp); + }).error(function() { + if (thisChangeId === changeCounter) clearContent(); + }); + scope.$emit('$includeContentRequested'); + } else { + clearContent(); + } + }); + }; + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngInit + * + * @description + * The `ngInit` directive specifies initialization tasks to be executed + * before the template enters execution mode during bootstrap. + * + * @element ANY + * @param {expression} ngInit {@link guide/expression Expression} to eval. + * + * @example + + +
+ {{greeting}} {{person}}! +
+
+ + it('should check greeting', function() { + expect(binding('greeting')).toBe('Hello'); + expect(binding('person')).toBe('World'); + }); + +
+ */ +var ngInitDirective = ngDirective({ + compile: function() { + return { + pre: function(scope, element, attrs) { + scope.$eval(attrs.ngInit); + } + } + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngNonBindable + * @priority 1000 + * + * @description + * Sometimes it is necessary to write code which looks like bindings but which should be left alone + * by angular. Use `ngNonBindable` to make angular ignore a chunk of HTML. + * + * @element ANY + * + * @example + * In this example there are two location where a simple binding (`{{}}`) is present, but the one + * wrapped in `ngNonBindable` is left alone. + * + * @example + + +
Normal: {{1 + 2}}
+
Ignored: {{1 + 2}}
+
+ + it('should check ng-non-bindable', function() { + expect(using('.doc-example-live').binding('1 + 2')).toBe('3'); + expect(using('.doc-example-live').element('div:last').text()). + toMatch(/1 \+ 2/); + }); + +
+ */ +var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); + +/** + * @ngdoc directive + * @name ng.directive:ngPluralize + * @restrict EA + * + * @description + * # Overview + * `ngPluralize` is a directive that displays messages according to en-US localization rules. + * These rules are bundled with angular.js and the rules can be overridden + * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive + * by specifying the mappings between + * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html + * plural categories} and the strings to be displayed. + * + * # Plural categories and explicit number rules + * There are two + * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html + * plural categories} in Angular's default en-US locale: "one" and "other". + * + * While a plural category may match many numbers (for example, in en-US locale, "other" can match + * any number that is not 1), an explicit number rule can only match one number. For example, the + * explicit number rule for "3" matches the number 3. You will see the use of plural categories + * and explicit number rules throughout later parts of this documentation. + * + * # Configuring ngPluralize + * You configure ngPluralize by providing 2 attributes: `count` and `when`. + * You can also provide an optional attribute, `offset`. + * + * The value of the `count` attribute can be either a string or an {@link guide/expression + * Angular expression}; these are evaluated on the current scope for its bound value. + * + * The `when` attribute specifies the mappings between plural categories and the actual + * string to be displayed. The value of the attribute should be a JSON object so that Angular + * can interpret it correctly. + * + * The following example shows how to configure ngPluralize: + * + *
+ * 
+ * 
+ *
+ * + * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not + * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" + * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for + * other numbers, for example 12, so that instead of showing "12 people are viewing", you can + * show "a dozen people are viewing". + * + * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted + * into pluralized strings. In the previous example, Angular will replace `{}` with + * `{{personCount}}`. The closed braces `{}` is a placeholder + * for {{numberExpression}}. + * + * # Configuring ngPluralize with offset + * The `offset` attribute allows further customization of pluralized text, which can result in + * a better user experience. For example, instead of the message "4 people are viewing this document", + * you might display "John, Kate and 2 others are viewing this document". + * The offset attribute allows you to offset a number by any desired value. + * Let's take a look at an example: + * + *
+ * 
+ * 
+ * 
+ * + * Notice that we are still using two plural categories(one, other), but we added + * three explicit number rules 0, 1 and 2. + * When one person, perhaps John, views the document, "John is viewing" will be shown. + * When three people view the document, no explicit number rule is found, so + * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. + * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing" + * is shown. + * + * Note that when you specify offsets, you must provide explicit number rules for + * numbers from 0 up to and including the offset. If you use an offset of 3, for example, + * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for + * plural categories "one" and "other". + * + * @param {string|expression} count The variable to be bounded to. + * @param {string} when The mapping between plural category to its corresponding strings. + * @param {number=} offset Offset to deduct from the total number. + * + * @example + + + +
+ Person 1:
+ Person 2:
+ Number of People:
+ + + Without Offset: + +
+ + + With Offset(2): + + +
+
+ + it('should show correct pluralized string', function() { + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('1 person is viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor is viewing.'); + + using('.doc-example-live').input('personCount').enter('0'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('Nobody is viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Nobody is viewing.'); + + using('.doc-example-live').input('personCount').enter('2'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('2 people are viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor and Misko are viewing.'); + + using('.doc-example-live').input('personCount').enter('3'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('3 people are viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor, Misko and one other person are viewing.'); + + using('.doc-example-live').input('personCount').enter('4'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('4 people are viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor, Misko and 2 other people are viewing.'); + }); + + it('should show data-binded names', function() { + using('.doc-example-live').input('personCount').enter('4'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor, Misko and 2 other people are viewing.'); + + using('.doc-example-live').input('person1').enter('Di'); + using('.doc-example-live').input('person2').enter('Vojta'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Di, Vojta and 2 other people are viewing.'); + }); + +
+ */ +var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { + var BRACE = /{}/g; + return { + restrict: 'EA', + link: function(scope, element, attr) { + var numberExp = attr.count, + whenExp = element.attr(attr.$attr.when), // this is because we have {{}} in attrs + offset = attr.offset || 0, + whens = scope.$eval(whenExp), + whensExpFns = {}, + startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(); + + forEach(whens, function(expression, key) { + whensExpFns[key] = + $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + + offset + endSymbol)); + }); + + scope.$watch(function ngPluralizeWatch() { + var value = parseFloat(scope.$eval(numberExp)); + + if (!isNaN(value)) { + //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, + //check it against pluralization rules in $locale service + if (!(value in whens)) value = $locale.pluralCat(value - offset); + return whensExpFns[value](scope, element, true); + } else { + return ''; + } + }, function ngPluralizeWatchAction(newVal) { + element.text(newVal); + }); + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngRepeat + * + * @description + * The `ngRepeat` directive instantiates a template once per item from a collection. Each template + * instance gets its own scope, where the given loop variable is set to the current collection item, + * and `$index` is set to the item index or key. + * + * Special properties are exposed on the local scope of each template instance, including: + * + * * `$index` – `{number}` – iterator offset of the repeated element (0..length-1) + * * `$first` – `{boolean}` – true if the repeated element is first in the iterator. + * * `$middle` – `{boolean}` – true if the repeated element is between the first and last in the iterator. + * * `$last` – `{boolean}` – true if the repeated element is last in the iterator. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**, + * **leave** and **move** effects. + * + * @animations + * enter - when a new item is added to the list or when an item is revealed after a filter + * leave - when an item is removed from the list or when an item is filtered out + * move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered + * + * @element ANY + * @scope + * @priority 1000 + * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These + * formats are currently supported: + * + * * `variable in expression` – where variable is the user defined loop variable and `expression` + * is a scope expression giving the collection to enumerate. + * + * For example: `track in cd.tracks`. + * + * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, + * and `expression` is the scope expression giving the collection to enumerate. + * + * For example: `(name, age) in {'adam':10, 'amalie':12}`. + * + * * `variable in expression track by tracking_expression` – You can also provide an optional tracking function + * which can be used to associate the objects in the collection with the DOM elements. If no tractking function + * is specified the ng-repeat associates elements by identity in the collection. It is an error to have + * more then one tractking function to resolve to the same key. (This would mean that two distinct objects are + * mapped to the same DOM element, which is not possible.) + * + * For example: `item in items` is equivalent to `item in items track by $id(item)'. This implies that the DOM elements + * will be associated by item identity in the array. + * + * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique + * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements + * with the corresponding item in the array by identity. Moving the same object in array would move the DOM + * element in the same way ian the DOM. + * + * For example: `item in items track by item.id` Is a typical pattern when the items come from the database. In this + * case the object identity does not matter. Two objects are considered equivalent as long as their `id` + * property is same. + * + * @example + * This example initializes the scope to a list of names and + * then uses `ngRepeat` to display every person: + + +
+ I have {{friends.length}} friends. They are: + +
    +
  • + [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. +
  • +
+
+
+ + .example-repeat-enter, + .example-repeat-leave, + .example-repeat-move { + -webkit-transition:all linear 0.5s; + -moz-transition:all linear 0.5s; + -ms-transition:all linear 0.5s; + -o-transition:all linear 0.5s; + transition:all linear 0.5s; + } + + .example-repeat-enter { + line-height:0; + opacity:0; + } + .example-repeat-enter.example-repeat-enter-active { + line-height:20px; + opacity:1; + } + + .example-repeat-leave { + opacity:1; + line-height:20px; + } + .example-repeat-leave.example-repeat-leave-active { + opacity:0; + line-height:0; + } + + .example-repeat-move { } + .example-repeat-move.example-repeat-move-active { } + + + it('should render initial data set', function() { + var r = using('.doc-example-live').repeater('ul li'); + expect(r.count()).toBe(10); + expect(r.row(0)).toEqual(["1","John","25"]); + expect(r.row(1)).toEqual(["2","Jessie","30"]); + expect(r.row(9)).toEqual(["10","Samantha","60"]); + expect(binding('friends.length')).toBe("10"); + }); + + it('should update repeater when filter predicate changes', function() { + var r = using('.doc-example-live').repeater('ul li'); + expect(r.count()).toBe(10); + + input('q').enter('ma'); + + expect(r.count()).toBe(2); + expect(r.row(0)).toEqual(["1","Mary","28"]); + expect(r.row(1)).toEqual(["2","Samantha","60"]); + }); + +
+ */ +var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { + var NG_REMOVED = '$$NG_REMOVED'; + return { + transclude: 'element', + priority: 1000, + terminal: true, + compile: function(element, attr, linker) { + return function($scope, $element, $attr){ + var animate = $animator($scope, $attr); + var expression = $attr.ngRepeat; + var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/), + trackByExp, trackByExpGetter, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier, + hashFnLocals = {$id: hashKey}; + + if (!match) { + throw Error("Expected ngRepeat in form of '_item_ in _collection_[ track by _id_]' but got '" + + expression + "'."); + } + + lhs = match[1]; + rhs = match[2]; + trackByExp = match[4]; + + if (trackByExp) { + trackByExpGetter = $parse(trackByExp); + trackByIdFn = function(key, value, index) { + // assign key, value, and $index to the locals so that they can be used in hash functions + if (keyIdentifier) hashFnLocals[keyIdentifier] = key; + hashFnLocals[valueIdentifier] = value; + hashFnLocals.$index = index; + return trackByExpGetter($scope, hashFnLocals); + }; + } else { + trackByIdFn = function(key, value) { + return hashKey(value); + } + } + + match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); + if (!match) { + throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" + + lhs + "'."); + } + valueIdentifier = match[3] || match[1]; + keyIdentifier = match[2]; + + // Store a list of elements from previous run. This is a hash where key is the item from the + // iterator, and the value is objects with following properties. + // - scope: bound scope + // - element: previous element. + // - index: position + var lastBlockMap = {}; + + //watch props + $scope.$watchCollection(rhs, function ngRepeatAction(collection){ + var index, length, + cursor = $element, // current position of the node + nextCursor, + // Same as lastBlockMap but it has the current state. It will become the + // lastBlockMap on the next iteration. + nextBlockMap = {}, + arrayLength, + childScope, + key, value, // key/value of iteration + trackById, + collectionKeys, + block, // last object information {scope, element, id} + nextBlockOrder = []; + + + if (isArrayLike(collection)) { + collectionKeys = collection; + } else { + // if object, extract keys, sort them and use to determine order of iteration over obj props + collectionKeys = []; + for (key in collection) { + if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { + collectionKeys.push(key); + } + } + collectionKeys.sort(); + } + + arrayLength = collectionKeys.length; + + // locate existing items + length = nextBlockOrder.length = collectionKeys.length; + for(index = 0; index < length; index++) { + key = (collection === collectionKeys) ? index : collectionKeys[index]; + value = collection[key]; + trackById = trackByIdFn(key, value, index); + if(lastBlockMap.hasOwnProperty(trackById)) { + block = lastBlockMap[trackById] + delete lastBlockMap[trackById]; + nextBlockMap[trackById] = block; + nextBlockOrder[index] = block; + } else if (nextBlockMap.hasOwnProperty(trackById)) { + // restore lastBlockMap + forEach(nextBlockOrder, function(block) { + if (block && block.element) lastBlockMap[block.id] = block; + }); + // This is a duplicate and we need to throw an error + throw new Error('Duplicates in a repeater are not allowed. Repeater: ' + expression + + ' key: ' + trackById); + } else { + // new never before seen block + nextBlockOrder[index] = { id: trackById }; + nextBlockMap[trackById] = false; + } + } + + // remove existing items + for (key in lastBlockMap) { + if (lastBlockMap.hasOwnProperty(key)) { + block = lastBlockMap[key]; + animate.leave(block.element); + block.element[0][NG_REMOVED] = true; + block.scope.$destroy(); + } + } + + // we are not using forEach for perf reasons (trying to avoid #call) + for (index = 0, length = collectionKeys.length; index < length; index++) { + key = (collection === collectionKeys) ? index : collectionKeys[index]; + value = collection[key]; + block = nextBlockOrder[index]; + + if (block.element) { + // if we have already seen this object, then we need to reuse the + // associated scope/element + childScope = block.scope; + + nextCursor = cursor[0]; + do { + nextCursor = nextCursor.nextSibling; + } while(nextCursor && nextCursor[NG_REMOVED]); + + if (block.element[0] == nextCursor) { + // do nothing + cursor = block.element; + } else { + // existing item which got moved + animate.move(block.element, null, cursor); + cursor = block.element; + } + } else { + // new item which we don't know about + childScope = $scope.$new(); + } + + childScope[valueIdentifier] = value; + if (keyIdentifier) childScope[keyIdentifier] = key; + childScope.$index = index; + childScope.$first = (index === 0); + childScope.$last = (index === (arrayLength - 1)); + childScope.$middle = !(childScope.$first || childScope.$last); + + if (!block.element) { + linker(childScope, function(clone) { + animate.enter(clone, null, cursor); + cursor = clone; + block.scope = childScope; + block.element = clone; + nextBlockMap[block.id] = block; + }); + } + } + lastBlockMap = nextBlockMap; + }); + }; + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngShow + * + * @description + * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) + * conditionally based on **"truthy"** values evaluated within an {expression}. In other + * words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible** + * (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none). + * With ngHide this is the reverse whereas true values cause the element itself to become + * hidden. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** + * and **hide** effects. + * + * @animations + * show - happens after the ngShow expression evaluates to a truthy value and the contents are set to visible + * hide - happens before the ngShow expression evaluates to a non truthy value and just before the contents are set to hidden + * + * @element ANY + * @param {expression} ngShow If the {@link guide/expression expression} is truthy + * then the element is shown or hidden respectively. + * + * @example + + + Click me:
+
+ Show: + + I show up when your checkbox is checked. + +
+
+ Hide: + + I hide when your checkbox is checked. + +
+
+ + .example-show, .example-hide { + -webkit-transition:all linear 0.5s; + -moz-transition:all linear 0.5s; + -ms-transition:all linear 0.5s; + -o-transition:all linear 0.5s; + transition:all linear 0.5s; + } + + .example-show { + line-height:0; + opacity:0; + padding:0 10px; + } + .example-show-active.example-show-active { + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; + } + + .example-hide { + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; + } + .example-hide-active.example-hide-active { + line-height:0; + opacity:0; + padding:0 10px; + } + + .check-element { + padding:10px; + border:1px solid black; + background:white; + } + + + it('should check ng-show / ng-hide', function() { + expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); + expect(element('.doc-example-live span:last:visible').count()).toEqual(1); + + input('checked').check(); + + expect(element('.doc-example-live span:first:visible').count()).toEqual(1); + expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); + }); + +
+ */ +//TODO(misko): refactor to remove element from the DOM +var ngShowDirective = ['$animator', function($animator) { + return function(scope, element, attr) { + var animate = $animator(scope, attr); + scope.$watch(attr.ngShow, function ngShowWatchAction(value){ + animate[toBoolean(value) ? 'show' : 'hide'](element); + }); + }; +}]; + + +/** + * @ngdoc directive + * @name ng.directive:ngHide + * + * @description + * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) + * conditionally based on **"truthy"** values evaluated within an {expression}. In other + * words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible** + * (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none). + * With ngHide this is the reverse whereas true values cause the element itself to become + * hidden. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** + * and **hide** effects. + * + * @animations + * show - happens after the ngHide expression evaluates to a non truthy value and the contents are set to visible + * hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden + * + * @element ANY + * @param {expression} ngHide If the {@link guide/expression expression} is truthy then + * the element is shown or hidden respectively. + * + * @example + + + Click me:
+
+ Show: + + I show up when your checkbox is checked. + +
+
+ Hide: + + I hide when your checkbox is checked. + +
+
+ + .example-show, .example-hide { + -webkit-transition:all linear 0.5s; + -moz-transition:all linear 0.5s; + -ms-transition:all linear 0.5s; + -o-transition:all linear 0.5s; + transition:all linear 0.5s; + } + + .example-show { + line-height:0; + opacity:0; + padding:0 10px; + } + .example-show.example-show-active { + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; + } + + .example-hide { + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; + } + .example-hide.example-hide-active { + line-height:0; + opacity:0; + padding:0 10px; + } + + .check-element { + padding:10px; + border:1px solid black; + background:white; + } + + + it('should check ng-show / ng-hide', function() { + expect(element('.doc-example-live .check-element:first:hidden').count()).toEqual(1); + expect(element('.doc-example-live .check-element:last:visible').count()).toEqual(1); + + input('checked').check(); + + expect(element('.doc-example-live .check-element:first:visible').count()).toEqual(1); + expect(element('.doc-example-live .check-element:last:hidden').count()).toEqual(1); + }); + +
+ */ +//TODO(misko): refactor to remove element from the DOM +var ngHideDirective = ['$animator', function($animator) { + return function(scope, element, attr) { + var animate = $animator(scope, attr); + scope.$watch(attr.ngHide, function ngHideWatchAction(value){ + animate[toBoolean(value) ? 'hide' : 'show'](element); + }); + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngStyle + * + * @description + * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. + * + * @element ANY + * @param {expression} ngStyle {@link guide/expression Expression} which evals to an + * object whose keys are CSS style names and values are corresponding values for those CSS + * keys. + * + * @example + + + + +
+ Sample Text +
myStyle={{myStyle}}
+
+ + span { + color: black; + } + + + it('should check ng-style', function() { + expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); + element('.doc-example-live :button[value=set]').click(); + expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)'); + element('.doc-example-live :button[value=clear]').click(); + expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); + }); + +
+ */ +var ngStyleDirective = ngDirective(function(scope, element, attr) { + scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { + if (oldStyles && (newStyles !== oldStyles)) { + forEach(oldStyles, function(val, style) { element.css(style, '');}); + } + if (newStyles) element.css(newStyles); + }, true); +}); + +/** + * @ngdoc directive + * @name ng.directive:ngSwitch + * @restrict EA + * + * @description + * The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression. + * Elements within ngSwitch but without ngSwitchWhen or ngSwitchDefault directives will be preserved at the location + * as specified in the template. + * + * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it + * from the template cache), ngSwitch simply choses one of the nested elements and makes it visible based on which element + * matches the value obtained from the evaluated expression. In other words, you define a container element + * (where you place the directive), place an expression on the **on="..." attribute** + * (or the **ng-switch="..." attribute**), define any inner elements inside of the directive and place + * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on + * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default + * attribute is displayed. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** + * and **leave** effects. + * + * @animations + * enter - happens after the ngSwtich contents change and the matched child element is placed inside the container + * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM + * + * @usage + * + * ... + * ... + * ... + * + * + * @scope + * @param {*} ngSwitch|on expression to match against ng-switch-when. + * @paramDescription + * On child elements add: + * + * * `ngSwitchWhen`: the case statement to match against. If match then this + * case will be displayed. If the same match appears multiple times, all the + * elements will be displayed. + * * `ngSwitchDefault`: the default case when no other case match. If there + * are multiple default cases, all of them will be displayed when no other + * case match. + * + * + * @example + + +
+ + selection={{selection}} +
+
+
Settings Div
+
Home Span
+
default
+
+
+
+ + function Ctrl($scope) { + $scope.items = ['settings', 'home', 'other']; + $scope.selection = $scope.items[0]; + } + + + .example-leave, .example-enter { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + } + + .example-animate-container > * { + display:block; + padding:10px; + } + + .example-enter { + top:-50px; + } + .example-enter.example-enter-active { + top:0; + } + + .example-leave { + top:0; + } + .example-leave.example-leave-active { + top:50px; + } + + + it('should start in settings', function() { + expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/); + }); + it('should change to home', function() { + select('selection').option('home'); + expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/); + }); + it('should select default', function() { + select('selection').option('other'); + expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/); + }); + +
+ */ +var ngSwitchDirective = ['$animator', function($animator) { + return { + restrict: 'EA', + require: 'ngSwitch', + + // asks for $scope to fool the BC controller module + controller: ['$scope', function ngSwitchController() { + this.cases = {}; + }], + link: function(scope, element, attr, ngSwitchController) { + var animate = $animator(scope, attr); + var watchExpr = attr.ngSwitch || attr.on, + selectedTranscludes, + selectedElements, + selectedScopes = []; + + scope.$watch(watchExpr, function ngSwitchWatchAction(value) { + for (var i= 0, ii=selectedScopes.length; i + + +
+
+
+ {{text}} +
+
+ + it('should have transcluded', function() { + input('title').enter('TITLE'); + input('text').enter('TEXT'); + expect(binding('title')).toEqual('TITLE'); + expect(binding('text')).toEqual('TEXT'); + }); + + + * + */ +var ngTranscludeDirective = ngDirective({ + controller: ['$transclude', '$element', function($transclude, $element) { + $transclude(function(clone) { + $element.append(clone); + }); + }] +}); + +/** + * @ngdoc directive + * @name ng.directive:ngView + * @restrict ECA + * + * @description + * # Overview + * `ngView` is a directive that complements the {@link ng.$route $route} service by + * including the rendered template of the current route into the main layout (`index.html`) file. + * Every time the current route changes, the included view changes with it according to the + * configuration of the `$route` service. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** + * and **leave** effects. + * + * @animations + * enter - happens just after the ngView contents are changed (when the new view DOM element is inserted into the DOM) + * leave - happens just after the current ngView contents change and just before the former contents are removed from the DOM + * + * @scope + * @example + + +
+ Choose: + Moby | + Moby: Ch1 | + Gatsby | + Gatsby: Ch4 | + Scarlet Letter
+ +
+
+ +
$location.path() = {{main.$location.path()}}
+
$route.current.templateUrl = {{main.$route.current.templateUrl}}
+
$route.current.params = {{main.$route.current.params}}
+
$route.current.scope.name = {{main.$route.current.scope.name}}
+
$routeParams = {{main.$routeParams}}
+
+
+ + +
+ controller: {{book.name}}
+ Book Id: {{book.params.bookId}}
+
+
+ + +
+ controller: {{chapter.name}}
+ Book Id: {{chapter.params.bookId}}
+ Chapter Id: {{chapter.params.chapterId}} +
+
+ + + .example-leave, .example-enter { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; + -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; + -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; + -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; + } + + .example-animate-container { + position:relative; + height:100px; + } + + .example-animate-container > * { + display:block; + width:100%; + border-left:1px solid black; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + padding:10px; + } + + .example-enter { + left:100%; + } + .example-enter.example-enter-active { + left:0; + } + + .example-leave { } + .example-leave.example-leave-active { + left:-100%; + } + + + + angular.module('ngView', [], function($routeProvider, $locationProvider) { + $routeProvider.when('/Book/:bookId', { + templateUrl: 'book.html', + controller: BookCntl, + controllerAs: 'book' + }); + $routeProvider.when('/Book/:bookId/ch/:chapterId', { + templateUrl: 'chapter.html', + controller: ChapterCntl, + controllerAs: 'chapter' + }); + + // configure html5 to get links working on jsfiddle + $locationProvider.html5Mode(true); + }); + + function MainCntl($route, $routeParams, $location) { + this.$route = $route; + this.$location = $location; + this.$routeParams = $routeParams; + } + + function BookCntl($routeParams) { + this.name = "BookCntl"; + this.params = $routeParams; + } + + function ChapterCntl($routeParams) { + this.name = "ChapterCntl"; + this.params = $routeParams; + } + + + + it('should load and compile correct template', function() { + element('a:contains("Moby: Ch1")').click(); + var content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: ChapterCntl/); + expect(content).toMatch(/Book Id\: Moby/); + expect(content).toMatch(/Chapter Id\: 1/); + + element('a:contains("Scarlet")').click(); + content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: BookCntl/); + expect(content).toMatch(/Book Id\: Scarlet/); + }); + +
+ */ + + +/** + * @ngdoc event + * @name ng.directive:ngView#$viewContentLoaded + * @eventOf ng.directive:ngView + * @eventType emit on the current ngView scope + * @description + * Emitted every time the ngView content is reloaded. + */ +var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile', + '$controller', '$animator', + function($http, $templateCache, $route, $anchorScroll, $compile, + $controller, $animator) { + return { + restrict: 'ECA', + terminal: true, + link: function(scope, element, attr) { + var lastScope, + onloadExp = attr.onload || '', + animate = $animator(scope, attr); + + scope.$on('$routeChangeSuccess', update); + update(); + + + function destroyLastScope() { + if (lastScope) { + lastScope.$destroy(); + lastScope = null; + } + } + + function clearContent() { + animate.leave(element.contents(), element); + destroyLastScope(); + } + + function update() { + var locals = $route.current && $route.current.locals, + template = locals && locals.$template; + + if (template) { + clearContent(); + var enterElements = jqLite('
').html(template).contents(); + animate.enter(enterElements, element); + + var link = $compile(enterElements), + current = $route.current, + controller; + + lastScope = current.scope = scope.$new(); + if (current.controller) { + locals.$scope = lastScope; + controller = $controller(current.controller, locals); + if (current.controllerAs) { + lastScope[current.controllerAs] = controller; + } + element.children().data('$ngControllerController', controller); + } + + link(lastScope); + lastScope.$emit('$viewContentLoaded'); + lastScope.$eval(onloadExp); + + // $anchorScroll might listen on event... + $anchorScroll(); + } else { + clearContent(); + } + } + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:script + * + * @description + * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the + * template can be used by `ngInclude`, `ngView` or directive templates. + * + * @restrict E + * @param {'text/ng-template'} type must be set to `'text/ng-template'` + * + * @example + + + + + Load inlined template +
+
+ + it('should load template defined inside script tag', function() { + element('#tpl-link').click(); + expect(element('#tpl-content').text()).toMatch(/Content of the template/); + }); + +
+ */ +var scriptDirective = ['$templateCache', function($templateCache) { + return { + restrict: 'E', + terminal: true, + compile: function(element, attr) { + if (attr.type == 'text/ng-template') { + var templateUrl = attr.id, + // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent + text = element[0].text; + + $templateCache.put(templateUrl, text); + } + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:select + * @restrict E + * + * @description + * HTML `SELECT` element with angular data-binding. + * + * # `ngOptions` + * + * Optionally `ngOptions` attribute can be used to dynamically generate a list of `
+ + + it('should check ng-options', function() { + expect(binding('{selected_color:color}')).toMatch('red'); + select('color').option('0'); + expect(binding('{selected_color:color}')).toMatch('black'); + using('.nullable').select('color').option(''); + expect(binding('{selected_color:color}')).toMatch('null'); + }); + + + */ + +var ngOptionsDirective = valueFn({ terminal: true }); +var selectDirective = ['$compile', '$parse', function($compile, $parse) { + //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000007777000000000000000000088888 + var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/, + nullModelCtrl = {$setViewValue: noop}; + + return { + restrict: 'E', + require: ['select', '?ngModel'], + controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { + var self = this, + optionsMap = {}, + ngModelCtrl = nullModelCtrl, + nullOption, + unknownOption; + + + self.databound = $attrs.ngModel; + + + self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { + ngModelCtrl = ngModelCtrl_; + nullOption = nullOption_; + unknownOption = unknownOption_; + } + + + self.addOption = function(value) { + optionsMap[value] = true; + + if (ngModelCtrl.$viewValue == value) { + $element.val(value); + if (unknownOption.parent()) unknownOption.remove(); + } + }; + + + self.removeOption = function(value) { + if (this.hasOption(value)) { + delete optionsMap[value]; + if (ngModelCtrl.$viewValue == value) { + this.renderUnknownOption(value); + } + } + }; + + + self.renderUnknownOption = function(val) { + var unknownVal = '? ' + hashKey(val) + ' ?'; + unknownOption.val(unknownVal); + $element.prepend(unknownOption); + $element.val(unknownVal); + unknownOption.prop('selected', true); // needed for IE + } + + + self.hasOption = function(value) { + return optionsMap.hasOwnProperty(value); + } + + $scope.$on('$destroy', function() { + // disable unknown option so that we don't do work when the whole select is being destroyed + self.renderUnknownOption = noop; + }); + }], + + link: function(scope, element, attr, ctrls) { + // if ngModel is not defined, we don't need to do anything + if (!ctrls[1]) return; + + var selectCtrl = ctrls[0], + ngModelCtrl = ctrls[1], + multiple = attr.multiple, + optionsExp = attr.ngOptions, + nullOption = false, // if false, user will not be able to select it (used by ngOptions) + emptyOption, + // we can't just jqLite('