From b71efac59f96fb8fe6cb5cc475b8d57aace80def Mon Sep 17 00:00:00 2001 From: nelsonlaquet Date: Thu, 1 Aug 2013 03:57:08 -0500 Subject: [PATCH] Many things --- app/commands/MigrateOldData.php | 64 + app/controllers/Api/Web/AlbumsController.php | 17 +- app/controllers/Api/Web/ArtistsController.php | 40 +- .../Api/Web/CommentsController.php | 42 + .../Api/Web/DashboardController.php | 31 +- .../Api/Web/FavouritesController.php | 12 + .../Api/Web/PlaylistsController.php | 29 +- app/controllers/Api/Web/TracksController.php | 17 +- app/controllers/TracksController.php | 15 + .../2013_06_07_003952_create_users_table.php | 2 +- .../2013_07_28_034328_create_songs_table.php | 4 +- .../2013_08_01_024827_create_favourites.php | 34 + .../2013_08_01_051337_create_comments.php | 37 + app/library/Assets.php | 2 + .../Commands/AddTrackToPlaylistCommand.php | 37 + app/models/Commands/CreateCommentCommand.php | 71 + .../Commands/ToggleFavouriteCommand.php | 49 + app/models/Entities/Album.php | 22 +- app/models/Entities/Comment.php | 62 + app/models/Entities/Favourite.php | 53 + app/models/Entities/Playlist.php | 10 +- app/models/Entities/Track.php | 36 +- app/models/Entities/User.php | 4 + app/routes.php | 10 + app/start/artisan.php | 1 + app/views/shared/_app_layout.blade.php | 21 +- public/.htaccess | 6 +- public/flash/soundmanager/soundmanager2.swf | Bin 0 -> 2910 bytes .../soundmanager/soundmanager2_debug.swf | Bin 0 -> 3284 bytes .../soundmanager/soundmanager2_flash9.swf | Bin 0 -> 8678 bytes .../soundmanager2_flash9_debug.swf | Bin 0 -> 17073 bytes .../soundmanager2_flash_xdomain.zip | Bin 0 -> 32734 bytes .../app/controllers/application.coffee | 12 +- .../app/controllers/artist-favourites.coffee | 7 +- .../app/controllers/playlist-form.coffee | 6 +- .../scripts/app/controllers/playlist.coffee | 19 +- public/scripts/app/controllers/track.coffee | 37 +- .../scripts/app/directives/albums-list.coffee | 5 +- public/scripts/app/directives/comments.coffee | 29 + .../app/directives/favouriteButton.coffee | 20 + public/scripts/app/directives/player.coffee | 73 + .../app/directives/progress-bar.coffee | 2 +- .../scripts/app/directives/tracks-list.coffee | 13 +- public/scripts/app/filters/newlines.coffee | 4 +- public/scripts/app/filters/noHTML.coffee | 2 + public/scripts/app/services/artists.coffee | 11 + public/scripts/app/services/comments.coffee | 27 + public/scripts/app/services/favourites.coffee | 13 + public/scripts/app/services/player.coffee | 117 + public/scripts/app/services/playlists.coffee | 18 + public/scripts/base/jquery.cookie.js | 95 + public/scripts/base/soundmanager2-nodebug.js | 2643 +++++++++++++++++ public/scripts/shared/init.coffee | 9 + public/styles/albums.less | 2 + public/styles/components.less | 2 +- public/styles/layout.less | 84 +- public/styles/tracks.less | 124 +- public/templates/albums/show.html | 19 +- public/templates/artists/_show_layout.html | 2 +- public/templates/artists/content.html | 4 +- public/templates/artists/favourites.html | 11 +- public/templates/artists/profile.html | 12 +- public/templates/auth/login.html | 5 +- public/templates/dashboard/index.html | 13 +- public/templates/directives/comments.html | 21 + .../directives/favourite-button.html | 10 + public/templates/directives/player.html | 31 + public/templates/directives/tracks-list.html | 12 +- public/templates/home/index.html | 40 +- public/templates/pages/about.html | 44 +- public/templates/pages/faq.html | 90 +- public/templates/playlists/show.html | 45 +- public/templates/tracks/_layout.html | 2 + public/templates/tracks/search-list.html | 2 +- public/templates/tracks/search.html | 10 +- public/templates/tracks/show.html | 31 +- vendor/autoload.php | 2 +- vendor/composer/autoload_classmap.php | 16 + vendor/composer/autoload_real.php | 6 +- 79 files changed, 4297 insertions(+), 233 deletions(-) create mode 100644 app/commands/MigrateOldData.php create mode 100644 app/controllers/Api/Web/CommentsController.php create mode 100644 app/controllers/Api/Web/FavouritesController.php create mode 100644 app/database/migrations/2013_08_01_024827_create_favourites.php create mode 100644 app/database/migrations/2013_08_01_051337_create_comments.php create mode 100644 app/models/Commands/AddTrackToPlaylistCommand.php create mode 100644 app/models/Commands/CreateCommentCommand.php create mode 100644 app/models/Commands/ToggleFavouriteCommand.php create mode 100644 app/models/Entities/Comment.php create mode 100644 app/models/Entities/Favourite.php create mode 100644 public/flash/soundmanager/soundmanager2.swf create mode 100644 public/flash/soundmanager/soundmanager2_debug.swf create mode 100644 public/flash/soundmanager/soundmanager2_flash9.swf create mode 100644 public/flash/soundmanager/soundmanager2_flash9_debug.swf create mode 100644 public/flash/soundmanager/soundmanager2_flash_xdomain.zip create mode 100644 public/scripts/app/directives/comments.coffee create mode 100644 public/scripts/app/directives/favouriteButton.coffee create mode 100644 public/scripts/app/directives/player.coffee create mode 100644 public/scripts/app/services/comments.coffee create mode 100644 public/scripts/app/services/favourites.coffee create mode 100644 public/scripts/app/services/player.coffee create mode 100644 public/scripts/base/jquery.cookie.js create mode 100644 public/scripts/base/soundmanager2-nodebug.js create mode 100644 public/scripts/shared/init.coffee create mode 100644 public/templates/directives/comments.html create mode 100644 public/templates/directives/favourite-button.html create mode 100644 public/templates/directives/player.html diff --git a/app/commands/MigrateOldData.php b/app/commands/MigrateOldData.php new file mode 100644 index 00000000..2cc265f4 --- /dev/null +++ b/app/commands/MigrateOldData.php @@ -0,0 +1,64 @@ +call('migrate:refresh'); + + $oldDb = DB::connection('old'); + $oldUsers = $oldDb->table('users')->get(); + + foreach ($oldUsers as $user) { + $displayName = $user->display_name; + if (!$displayName) + $displayName = $user->username; + + if (!$displayName) + $displayName = $user->mlpforums_name; + + if (!$displayName) + continue; + + DB::table('users')->insert([ + 'id' => $user->id, + 'display_name' => $displayName, + 'email' => $user->email, + 'created_at' => $user->created_at, + 'updated_at' => $user->updated_at, + 'slug' => $user->slug, + 'password_hash' => $user->password_hash, + 'password_salt' => $user->password_salt, + 'bio' => $user->bio, + 'sync_names' => $user->sync_names, + 'can_see_explicit_content' => $user->can_see_explicit_content, + 'mlpforums_name' => $user->mlpforums_name, + 'uses_gravatar' => $user->uses_gravatar, + 'gravatar' => $user->gravatar + ]); + } + } + + protected function getArguments() + { + return [ + ['example', InputArgument::REQUIRED, 'An example argument.'], + ]; + } + + protected function getOptions() { + return [ + ['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null], + ]; + } +} \ No newline at end of file diff --git a/app/controllers/Api/Web/AlbumsController.php b/app/controllers/Api/Web/AlbumsController.php index 5f11f995..55a844ad 100644 --- a/app/controllers/Api/Web/AlbumsController.php +++ b/app/controllers/Api/Web/AlbumsController.php @@ -4,11 +4,9 @@ use Commands\CreateAlbumCommand; use Commands\DeleteAlbumCommand; - use Commands\DeleteTrackCommand; use Commands\EditAlbumCommand; - use Commands\EditTrackCommand; - use Cover; use Entities\Album; + use Entities\Comment; use Entities\Image; use Entities\Track; use Illuminate\Support\Facades\Auth; @@ -29,7 +27,7 @@ } public function getShow($id) { - $album = Album::with('tracks', 'user')->find($id); + $album = Album::with(['tracks', 'user', 'comments' => function($query) { $query->with('user'); }])->details()->find($id); if (!$album) App::abort(404); @@ -47,6 +45,11 @@ ]; } + $comments = []; + foreach ($album->comments as $comment) { + $comments[] = Comment::mapPublic($comment); + } + return Response::json([ 'album' => [ 'id' => $album->id, @@ -71,7 +74,8 @@ 'views' => 0, 'downloads' => 0 ], - 'comments' => ['count' => 0, 'list' => []] + 'comments' => ['count' => count($comments), 'list' => $comments], + 'is_favourited' => $album->favourites->count() > 0 ] ], 200); } @@ -82,7 +86,8 @@ $page = Input::get('page'); $query = Album::summary() - ->with('tracks', 'user') + ->with(['tracks' => function($query) { $query->details(); }, 'user']) + ->details() ->orderBy('created_at', 'desc') ->whereRaw('(SELECT COUNT(id) FROM tracks WHERE tracks.album_id = albums.id) > 0'); diff --git a/app/controllers/Api/Web/ArtistsController.php b/app/controllers/Api/Web/ArtistsController.php index cae659ad..82b3ec78 100644 --- a/app/controllers/Api/Web/ArtistsController.php +++ b/app/controllers/Api/Web/ArtistsController.php @@ -9,6 +9,8 @@ use Commands\EditTrackCommand; use Cover; use Entities\Album; + use Entities\Comment; + use Entities\Favourite; use Entities\Image; use Entities\Track; use Entities\User; @@ -17,6 +19,33 @@ use Illuminate\Support\Facades\Response; class ArtistsController extends \ApiControllerBase { + public function getFavourites($slug) { + $user = User::whereSlug($slug)->first(); + if (!$user) + App::abort(404); + + $favs = Favourite::whereUserId($user->id)->with([ + 'track' => function($query) { $query->details(); }, + 'album' => function($query) { $query->details(); }])->get(); + + $tracks = []; + $albums = []; + + foreach ($favs as $fav) { + if ($fav->type == 'Entities\Track') { + $tracks[] = Track::mapPublicTrackSummary($fav->track); + } + else if ($fav->type == 'Entities\Album') { + $albums[] = Album::mapPublicAlbumSummary($fav->album); + } + } + + return Response::json([ + 'tracks' => $tracks, + 'albums' => $albums + ], 200); + } + public function getContent($slug) { $user = User::whereSlug($slug)->first(); if (!$user) @@ -49,17 +78,22 @@ } public function getShow($slug) { - $user = User::whereSlug($slug)->first(); + $user = User::whereSlug($slug)->with(['comments' => function ($query) { $query->with('user'); }])->first(); if (!$user) App::abort(404); - $trackQuery = Track::summary()->whereUserId($user->id)->whereNotNull('published_at')->orderBy('created_at', 'desc')->take(10); + $trackQuery = Track::summary()->whereUserId($user->id)->whereNotNull('published_at')->orderBy('created_at', 'desc')->take(20); $latestTracks = []; foreach ($trackQuery->get() as $track) { $latestTracks[] = Track::mapPublicTrackSummary($track); } + $comments = []; + foreach ($user->comments as $comment) { + $comments[] = Comment::mapPublic($comment); + } + return Response::json([ 'artist' => [ 'id' => $user->id, @@ -73,7 +107,7 @@ 'followers' => [], 'following' => [], 'latest_tracks' => $latestTracks, - 'comments' => ['count' => 0, 'list' => []], + 'comments' => ['count' => count($comments), 'list' => $comments], 'bio' => $user->bio, 'mlpforums_username' => $user->mlpforums_name ] diff --git a/app/controllers/Api/Web/CommentsController.php b/app/controllers/Api/Web/CommentsController.php new file mode 100644 index 00000000..afa82d03 --- /dev/null +++ b/app/controllers/Api/Web/CommentsController.php @@ -0,0 +1,42 @@ +execute(new CreateCommentCommand($type, $id, Input::all())); + } + + public function getIndex($type, $id) { + $column = ''; + + if ($type == 'track') + $column = 'track_id'; + else if ($type == 'user') + $column = 'profile_id'; + else if ($type == 'album') + $column = 'album_id'; + else if ($type == 'playlist') + $column = 'playlist_id'; + else + App::abort(500); + + $query = Comment::where($column, '=', $id)->orderBy('created_at', 'desc')->with('user'); + $comments = []; + + foreach ($query->get() as $comment) { + $comments[] = Comment::mapPublic($comment); + } + + return Response::json(['list' => $comments, 'count' => count($comments)]); + } + } \ No newline at end of file diff --git a/app/controllers/Api/Web/DashboardController.php b/app/controllers/Api/Web/DashboardController.php index 7fba4712..4a687794 100644 --- a/app/controllers/Api/Web/DashboardController.php +++ b/app/controllers/Api/Web/DashboardController.php @@ -14,41 +14,14 @@ class DashboardController extends \ApiControllerBase { public function getIndex() { - $query = Track::summary()->with(['genre', 'user', 'cover'])->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(15); + $query = Track::summary()->with(['genre', 'user', 'cover'])->details()->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(30); if (!Auth::check() || !Auth::user()->can_see_explicit_content) $query->whereIsExplicit(false); $tracks = []; foreach ($query->get() as $track) { - $tracks[] = [ - 'id' => $track->id, - 'title' => $track->title, - 'user' => [ - 'id' => $track->user->id, - 'name' => $track->user->display_name, - 'url' => $track->user->url - ], - 'url' => $track->url, - 'slug' => $track->slug, - 'is_vocal' => $track->is_vocal, - 'is_explicit' => $track->is_explicit, - 'is_downloadable' => $track->is_downloadable, - 'is_published' => $track->isPublished(), - 'published_at' => $track->published_at, - 'duration' => $track->duration, - 'genre' => [ - 'id' => $track->genre->id, - 'slug' => $track->genre->slug, - 'name' => $track->genre->name - ], - 'track_type_id' => $track->track_type_id, - 'covers' => [ - 'thumbnail' => $track->getCoverUrl(Image::THUMBNAIL), - 'small' => $track->getCoverUrl(Image::SMALL), - 'normal' => $track->getCoverUrl(Image::NORMAL) - ] - ]; + $tracks[] = Track::mapPublicTrackSummary($track); } return Response::json([ diff --git a/app/controllers/Api/Web/FavouritesController.php b/app/controllers/Api/Web/FavouritesController.php new file mode 100644 index 00000000..de728c73 --- /dev/null +++ b/app/controllers/Api/Web/FavouritesController.php @@ -0,0 +1,12 @@ +execute(new ToggleFavouriteCommand(Input::get('type'), Input::get('id'))); + } + } \ No newline at end of file diff --git a/app/controllers/Api/Web/PlaylistsController.php b/app/controllers/Api/Web/PlaylistsController.php index e7d7d630..1a303493 100644 --- a/app/controllers/Api/Web/PlaylistsController.php +++ b/app/controllers/Api/Web/PlaylistsController.php @@ -2,18 +2,13 @@ namespace Api\Web; - use Commands\CreateAlbumCommand; + use Commands\AddTrackToPlaylistCommand; use Commands\CreatePlaylistCommand; - use Commands\DeleteAlbumCommand; use Commands\DeletePlaylistCommand; - use Commands\DeleteTrackCommand; - use Commands\EditAlbumCommand; use Commands\EditPlaylistCommand; - use Commands\EditTrackCommand; - use Cover; use Entities\Album; + use Entities\Comment; use Entities\Image; - use Entities\PinnedPlaylist; use Entities\Playlist; use Entities\Track; use Illuminate\Support\Facades\Auth; @@ -33,11 +28,25 @@ return $this->execute(new DeletePlaylistCommand($id, Input::all())); } + public function postAddTrack($id) { + return $this->execute(new AddTrackToPlaylistCommand($id, Input::get('track_id'))); + } + public function getShow($id) { - $playlist = Playlist::find($id); + $playlist = Playlist::with(['tracks' => function($query) { $query->details(); }, 'comments' => function($query) { $query->with('user'); }])->find($id); if (!$playlist || !$playlist->canView(Auth::user())) App::abort('404'); + $tracks = []; + foreach ($playlist->tracks as $track) { + $tracks[] = Track::mapPublicTrackSummary($track); + } + + $comments = []; + foreach ($playlist->comments as $comment) { + $comments[] = Comment::mapPublic($comment); + } + return Response::json([ 'id' => $playlist->id, 'title' => $playlist->title, @@ -50,7 +59,9 @@ 'normal' => $playlist->getCoverUrl(Image::NORMAL) ], 'is_pinned' => true, - 'is_public' => $playlist->is_public == 1 + 'is_public' => $playlist->is_public == 1, + 'tracks' => $tracks, + 'comments' => ['count' => count($comments), 'list' => $comments], ], 200); } diff --git a/app/controllers/Api/Web/TracksController.php b/app/controllers/Api/Web/TracksController.php index ae8e15bc..4887155d 100644 --- a/app/controllers/Api/Web/TracksController.php +++ b/app/controllers/Api/Web/TracksController.php @@ -6,6 +6,7 @@ use Commands\EditTrackCommand; use Commands\UploadTrackCommand; use Cover; + use Entities\Favourite; use Entities\Image; use Entities\Track; use Illuminate\Support\Facades\Auth; @@ -27,7 +28,7 @@ } public function getShow($id) { - $track = Track::find($id); + $track = Track::details()->withComments()->find($id); if (!$track || !$track->canView(Auth::user())) return $this->notFound('Track not found!'); @@ -35,7 +36,7 @@ } public function getRecent() { - $query = Track::summary()->with(['genre', 'user', 'cover'])->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(15); + $query = Track::summary()->details()->with(['genre', 'user', 'cover'])->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(30); if (!Auth::check() || !Auth::user()->can_see_explicit_content) $query->whereIsExplicit(false); @@ -54,15 +55,23 @@ if (Input::has('page')) $page = Input::get('page'); - $query = Track::summary()->whereNotNull('published_at'); + $query = Track::summary() + ->details() + ->whereNotNull('published_at') + ->with('user', 'genre'); + $this->applyFilters($query); $totalCount = $query->count(); $query->take(30)->skip(30 * ($page - 1)); $tracks = []; - foreach ($query->get() as $track) + $ids = []; + + foreach ($query->get() as $track) { $tracks[] = Track::mapPublicTrackSummary($track); + $ids[] = $track->id; + } return Response::json(["tracks" => $tracks, "current_page" => $page, "total_pages" => ceil($totalCount / 30)], 200); } diff --git a/app/controllers/TracksController.php b/app/controllers/TracksController.php index f863d10f..fc18f02e 100644 --- a/app/controllers/TracksController.php +++ b/app/controllers/TracksController.php @@ -26,4 +26,19 @@ return Redirect::action('TracksController@getTrack', [$id, $track->slug]); } + + public function getStream($id) { + $track = Track::find($id); + if (!$track || !$track->canView(Auth::user())) + App::abort(404); + + $format = Track::$Formats['MP3']; + + $response = Response::make('', 200); + $response->header('X-Sendfile', $track->getFileFor('MP3')); + $response->header('Content-Disposition', 'filename=' . $track->getFilenameFor('MP3')); + $response->header('Content-Type', $format['mime_type']); + + return $response; + } } \ No newline at end of file diff --git a/app/database/migrations/2013_06_07_003952_create_users_table.php b/app/database/migrations/2013_06_07_003952_create_users_table.php index e6eb4f78..a9b97a88 100644 --- a/app/database/migrations/2013_06_07_003952_create_users_table.php +++ b/app/database/migrations/2013_06_07_003952_create_users_table.php @@ -12,7 +12,7 @@ class CreateUsersTable extends Migration { $table->boolean('sync_names')->default(true); $table->string('password_hash', 32); $table->string('password_salt', 5); - $table->string('email', 150)->unique(); + $table->string('email', 150)->indexed(); $table->string('gravatar')->nullable(); $table->string('slug'); $table->boolean('uses_gravatar')->default(true); diff --git a/app/database/migrations/2013_07_28_034328_create_songs_table.php b/app/database/migrations/2013_07_28_034328_create_songs_table.php index b34e718c..9b7e24f2 100644 --- a/app/database/migrations/2013_07_28_034328_create_songs_table.php +++ b/app/database/migrations/2013_07_28_034328_create_songs_table.php @@ -2040,8 +2040,8 @@ Twilight Sparkle: Yes! Everything’s going to be just fine!", public function down() { Schema::table('show_song_track', function($table){ - $table->drop_foreign('show_song_track_track_id_foreign'); - $table->drop_foreign('show_song_track_show_song_id_foreign'); + $table->dropForeign('show_song_track_track_id_foreign'); + $table->dropForeign('show_song_track_show_song_id_foreign'); }); Schema::drop('show_song_track'); diff --git a/app/database/migrations/2013_08_01_024827_create_favourites.php b/app/database/migrations/2013_08_01_024827_create_favourites.php new file mode 100644 index 00000000..e426cb8c --- /dev/null +++ b/app/database/migrations/2013_08_01_024827_create_favourites.php @@ -0,0 +1,34 @@ +increments('id'); + $table->integer('user_id')->unsigned()->index(); + + $table->integer('track_id')->unsigned()->nullable()->index(); + $table->integer('album_id')->unsigned()->nullable()->index(); + $table->integer('playlist_id')->unsigned()->nullable()->index(); + + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users')->on_delete('cascade'); + $table->foreign('track_id')->references('id')->on('tracks'); + $table->foreign('album_id')->references('id')->on('albums'); + $table->foreign('playlist_id')->references('id')->on('playlists'); + }); + } + + public function down() { + Schema::table('favourites', function($table){ + $table->dropForeign('favourites_user_id_foreign'); + $table->dropForeign('favourites_track_id_foreign'); + $table->dropForeign('favourites_album_id_foreign'); + $table->dropForeign('favourites_playlist_id_foreign'); + }); + + Schema::drop('favourites'); + } +} \ No newline at end of file diff --git a/app/database/migrations/2013_08_01_051337_create_comments.php b/app/database/migrations/2013_08_01_051337_create_comments.php new file mode 100644 index 00000000..0840c1ce --- /dev/null +++ b/app/database/migrations/2013_08_01_051337_create_comments.php @@ -0,0 +1,37 @@ +increments('id'); + $table->integer('user_id')->unsigned(); + $table->timestamps(); + $table->string('ip_address', 46); + $table->text('content'); + $table->timestamp('deleted_at')->nullable()->index(); + + $table->integer('profile_id')->unsigned()->nullable()->index(); + $table->integer('track_id')->unsigned()->nullable()->index(); + $table->integer('album_id')->unsigned()->nullable()->index(); + $table->integer('playlist_id')->unsigned()->nullable()->index(); + + $table->foreign('profile_id')->references('id')->on('users'); + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('track_id')->references('id')->on('tracks'); + $table->foreign('album_id')->references('id')->on('albums'); + $table->foreign('playlist_id')->references('id')->on('playlists'); + }); + } + + public function down() { + Schema::table('comments', function($table){ + $table->dropForeign('comments_user_id_foreign'); + $table->dropForeign('comments_track_id_foreign'); + $table->dropForeign('comments_album_id_foreign'); + $table->dropForeign('comments_playlist_id_foreign'); + }); + Schema::drop('comments'); + } +} \ No newline at end of file diff --git a/app/library/Assets.php b/app/library/Assets.php index b0427ab6..0f925a89 100644 --- a/app/library/Assets.php +++ b/app/library/Assets.php @@ -45,9 +45,11 @@ return new AssetCollection([ new FileAsset('scripts/base/jquery-2.0.2.js'), new FileAsset('scripts/base/jquery-ui.js'), + new FileAsset('scripts/base/jquery.cookie.js'), new FileAsset('scripts/base/jquery.colorbox.js'), new FileAsset('scripts/base/underscore.js'), new FileAsset('scripts/base/moment.js'), + new FileAsset('scripts/base/soundmanager2-nodebug.js'), new FileAsset('scripts/base/angular.js'), new FileAsset('scripts/base/ui-bootstrap-tpls-0.4.0.js'), new FileAsset('scripts/base/angular-ui-sortable.js'), diff --git a/app/models/Commands/AddTrackToPlaylistCommand.php b/app/models/Commands/AddTrackToPlaylistCommand.php new file mode 100644 index 00000000..36a2f737 --- /dev/null +++ b/app/models/Commands/AddTrackToPlaylistCommand.php @@ -0,0 +1,37 @@ +_playlist = Playlist::find($playlistId); + $this->_track = Track::find($trackId); + } + + /** + * @return bool + */ + public function authorize() { + $user = Auth::user(); + return $user != null && $this->_playlist && $this->_track && $this->_playlist->user_id == $user->id; + } + + /** + * @throws \Exception + * @return CommandResponse + */ + public function execute() { + $songIndex = $this->_playlist->tracks()->count() + 1; + $this->_playlist->tracks()->attach($this->_track, ['position' => $songIndex]); + return CommandResponse::succeed(['message' => 'Track added!']); + } + } \ No newline at end of file diff --git a/app/models/Commands/CreateCommentCommand.php b/app/models/Commands/CreateCommentCommand.php new file mode 100644 index 00000000..a18e7744 --- /dev/null +++ b/app/models/Commands/CreateCommentCommand.php @@ -0,0 +1,71 @@ +_input = $input; + $this->_id = $id; + $this->_type = $type; + } + + /** + * @return bool + */ + public function authorize() { + $user = \Auth::user(); + return $user != null; + } + + /** + * @throws \Exception + * @return CommandResponse + */ + public function execute() { + $rules = [ + 'content' => 'required', + 'track_id' => 'exists:tracks,id', + 'albums_id' => 'exists:albums,id', + 'playlist_id' => 'exists:playlists,id', + 'profile_id' => 'exists:users,id', + ]; + + $validator = Validator::make($this->_input, $rules); + + if ($validator->fails()) + return CommandResponse::fail($validator); + + $comment = new Comment(); + $comment->user_id = Auth::user()->id; + $comment->content = $this->_input['content']; + + if ($this->_type == 'track') + $column = 'track_id'; + else if ($this->_type == 'user') + $column = 'profile_id'; + else if ($this->_type == 'album') + $column = 'album_id'; + else if ($this->_type == 'playlist') + $column = 'playlist_id'; + else + App::abort(500); + + $comment->$column = $this->_id; + $comment->save(); + + return CommandResponse::succeed(Comment::mapPublic($comment)); + } + } \ No newline at end of file diff --git a/app/models/Commands/ToggleFavouriteCommand.php b/app/models/Commands/ToggleFavouriteCommand.php new file mode 100644 index 00000000..427d3285 --- /dev/null +++ b/app/models/Commands/ToggleFavouriteCommand.php @@ -0,0 +1,49 @@ +_resourceId = $resourceId; + $this->_resourceType = $resourceType; + } + + /** + * @return bool + */ + public function authorize() { + $user = Auth::user(); + return$user != null; + } + + /** + * @throws \Exception + * @return CommandResponse + */ + public function execute() { + $typeId = $this->_resourceType . '_id'; + $existing = Favourite::where($typeId, '=', $this->_resourceId)->first(); + $isFavourited = false; + + if ($existing) { + $existing->delete(); + } else { + $fav = new Favourite(); + $fav->$typeId = $this->_resourceId; + $fav->user_id = Auth::user()->id; + $fav->save(); + $isFavourited = true; + } + + return CommandResponse::succeed(['is_favourited' => $isFavourited]); + } + } \ No newline at end of file diff --git a/app/models/Entities/Album.php b/app/models/Entities/Album.php index b62c8b7b..cdc6709f 100644 --- a/app/models/Entities/Album.php +++ b/app/models/Entities/Album.php @@ -3,6 +3,7 @@ namespace Entities; use Cover; + use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\URL; use Whoops\Example\Exception; use Traits\SlugTrait; @@ -16,12 +17,26 @@ return self::select('id', 'title', 'user_id', 'slug', 'created_at', 'cover_id'); } + public function scopeDetails($query) { + if (Auth::check()) { + $query->with(['favourites' => function($query) { + $query->whereUserId(Auth::user()->id); + }]); + } + + return !$query; + } + protected $table = 'albums'; public function user() { return $this->belongsTo('Entities\User'); } + public function favourites() { + return $this->hasMany('Entities\Favourite'); + } + public function cover() { return $this->belongsTo('Entities\Image'); } @@ -30,6 +45,10 @@ return $this->hasMany('Entities\Track')->orderBy('track_number', 'asc'); } + public function comments(){ + return $this->hasMany('Entities\Comment'); + } + public static function mapPublicAlbumSummary($album) { return [ 'id' => $album->id, @@ -46,7 +65,8 @@ 'id' => $album->user->id, 'name' => $album->user->display_name, 'url' => $album->user->url, - ] + ], + 'is_favourited' => $album->favourites->count() > 0 ]; } diff --git a/app/models/Entities/Comment.php b/app/models/Entities/Comment.php new file mode 100644 index 00000000..0a5d4c42 --- /dev/null +++ b/app/models/Entities/Comment.php @@ -0,0 +1,62 @@ +belongsTo('Entities\User'); + } + + public function track(){ + return $this->belongsTo('Entities\Track'); + } + + public function album(){ + return $this->belongsTo('Entities\Album'); + } + + public function playlist(){ + return $this->belongsTo('Entities\Playlist'); + } + + public function profile(){ + return $this->belongsTo('Entities\User', 'profile_id'); + } + + public static function mapPublic($comment) { + return [ + 'id' => $comment->id, + 'created_at' => $comment->created_at, + 'content' => $comment->content, + 'user' => [ + 'name' => $comment->user->display_name, + 'id' => $comment->user->id, + 'url' => $comment->user->url, + 'avatars' => [ + 'normal' => $comment->user->getAvatarUrl(Image::NORMAL), + 'thumbnail' => $comment->user->getAvatarUrl(Image::THUMBNAIL), + 'small' => $comment->user->getAvatarUrl(Image::SMALL), + ] + ] + ]; + } + + public function getResourceAttribute(){ + if($this->track_id !== NULL) + return $this->track; + + else if($this->album_id !== NULL) + return $this->album; + + else if($this->playlist_id !== NULL) + return $this->playlist; + + else if($this->profile_id !== NULL) + return $this->profile; + + else return NULL; + } + } \ No newline at end of file diff --git a/app/models/Entities/Favourite.php b/app/models/Entities/Favourite.php new file mode 100644 index 00000000..aa66d420 --- /dev/null +++ b/app/models/Entities/Favourite.php @@ -0,0 +1,53 @@ +belongsTo('Entities\User'); + } + + public function track(){ + return $this->belongsTo('Entities\Track'); + } + + public function album(){ + return $this->belongsTo('Entities\Album'); + } + + public function playlist(){ + return $this->belongsTo('Entities\Playlist'); + } + + /** + * Return the resource associated with this favourite. + * + * @return Resource|NULL + */ + public function getResourceAttribute(){ + if ($this->track_id) + return $this->track; + + else if($this->album_id) + return $this->album; + + else if($this->playlist_id) + return $this->playlist; + + // no resource - this should never happen under real circumstances + else + return NULL; + } + + public function getTypeAttribute(){ + return get_class($this->resource); + } + } \ No newline at end of file diff --git a/app/models/Entities/Playlist.php b/app/models/Entities/Playlist.php index 08072dc2..ebf6ae95 100644 --- a/app/models/Entities/Playlist.php +++ b/app/models/Entities/Playlist.php @@ -14,7 +14,15 @@ } public function tracks() { - return $this->belongsToMany('Entities\Track')->orderBy('position', 'asc'); + return $this + ->belongsToMany('Entities\Track') + ->withPivot('position') + ->withTimestamps() + ->orderBy('position', 'asc'); + } + + public function comments(){ + return $this->hasMany('Entities\Comment'); } public function pins() { diff --git a/app/models/Entities/Track.php b/app/models/Entities/Track.php index d805686c..0a38ad30 100644 --- a/app/models/Entities/Track.php +++ b/app/models/Entities/Track.php @@ -6,6 +6,7 @@ use External; use getid3_writetags; use Helpers; + use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\URL; use Illuminate\Support\Str; @@ -29,6 +30,20 @@ return self::select('id', 'title', 'user_id', 'slug', 'is_vocal', 'is_explicit', 'created_at', 'published_at', 'duration', 'is_downloadable', 'genre_id', 'track_type_id', 'cover_id', 'album_id'); } + public function scopeDetails($query) { + if (Auth::check()) { + $query->with(['favourites' => function($query) { + $query->whereUserId(Auth::user()->id); + }]); + } + + return !$query; + } + + public function scopeWithComments($query) { + $query->with(['comments' => function($query) { $query->with('user'); }]); + } + public static function mapPublicTrackShow($track) { $returnValue = self::mapPublicTrackSummary($track); $returnValue['description'] = $track->description; @@ -38,7 +53,14 @@ 'plays' => 0, 'downloads' => 0 ]; - $returnValue['comments'] = ['count' => 0, 'list' => []]; + + $comments = []; + + foreach ($track->comments as $comment) { + $comments[] = Comment::mapPublic($comment); + } + + $returnValue['comments'] = ['count' => count($comments), 'list' => $comments]; if ($track->album_id != null) { $returnValue['album'] = [ @@ -94,7 +116,9 @@ 'thumbnail' => $track->getCoverUrl(Image::THUMBNAIL), 'small' => $track->getCoverUrl(Image::SMALL), 'normal' => $track->getCoverUrl(Image::NORMAL) - ] + ], + 'is_favourited' => $track->favourites->count() > 0, + 'duration' => $track->duration ]; } @@ -142,6 +166,14 @@ return $this->belongsTo('Entities\Genre'); } + public function comments(){ + return $this->hasMany('Entities\Comment'); + } + + public function favourites() { + return $this->hasMany('Entities\Favourite'); + } + public function cover() { return $this->belongsTo('Entities\Image'); } diff --git a/app/models/Entities/User.php b/app/models/Entities/User.php index 465912df..ddf09440 100644 --- a/app/models/Entities/User.php +++ b/app/models/Entities/User.php @@ -18,6 +18,10 @@ return $this->belongsTo('Entities\Image'); } + public function comments(){ + return $this->hasMany('Entities\Comment', 'profile_id'); + } + public function getUrlAttribute() { return URL::to('/' . $this->slug); } diff --git a/app/routes.php b/app/routes.php index 1c812470..69c9e525 100644 --- a/app/routes.php +++ b/app/routes.php @@ -18,6 +18,7 @@ Route::get('tracks/{id}-{slug}', 'TracksController@getTrack'); Route::get('t{id}', 'TracksController@getShortlink' ); + Route::get('t{id}/stream', 'TracksController@getStream' ); Route::get('t{id}/dl.{extension}', 'TracksController@getDownload' ); Route::get('albums', 'AlbumsController@getIndex'); @@ -52,6 +53,10 @@ Route::get('/albums', 'Api\Web\AlbumsController@getIndex'); Route::get('/albums/{id}', 'Api\Web\AlbumsController@getShow')->where('id', '\d+'); + Route::get('/playlists/{id}', 'Api\Web\PlaylistsController@getShow')->where('id', '\d+'); + + Route::get('/comments/{type}/{id}', 'Api\Web\CommentsController@getIndex')->where('id', '\d+'); + Route::get('/artists', 'Api\Web\ArtistsController@getIndex'); Route::get('/artists/{slug}', 'Api\Web\ArtistsController@getShow'); Route::get('/artists/{slug}/content', 'Api\Web\ArtistsController@getContent'); @@ -71,8 +76,13 @@ Route::post('/playlists/create', 'Api\Web\PlaylistsController@postCreate'); Route::post('/playlists/delete/{id}', 'Api\Web\PlaylistsController@postDelete'); Route::post('/playlists/edit/{id}', 'Api\Web\PlaylistsController@postEdit'); + Route::post('/playlists/{id}/add-track', 'Api\Web\PlaylistsController@postAddTrack'); + + Route::post('/comments/{type}/{id}', 'Api\Web\CommentsController@postCreate')->where('id', '\d+'); Route::post('/account/settings/save', 'Api\Web\AccountController@postSave'); + + Route::post('/favourites/toggle', 'Api\Web\FavouritesController@postToggle'); }); Route::group(['before' => 'auth'], function() { diff --git a/app/start/artisan.php b/app/start/artisan.php index 1df850bc..58867910 100644 --- a/app/start/artisan.php +++ b/app/start/artisan.php @@ -11,3 +11,4 @@ | */ + Artisan::add(new MigrateOldData); \ No newline at end of file diff --git a/app/views/shared/_app_layout.blade.php b/app/views/shared/_app_layout.blade.php index f896d5d0..2518ac28 100644 --- a/app/views/shared/_app_layout.blade.php +++ b/app/views/shared/_app_layout.blade.php @@ -5,22 +5,7 @@

Pony.fm

-
-
-
-
-
-
    -
  • -
  • -
  • -
  • -
- -
+
@@ -65,7 +50,7 @@ Account -
  • Favourites
  • + {{--
  • Favourites
  • --}}
  • Your Content
  • Settings
  • @endif @@ -74,7 +59,7 @@ @if (!Auth::check())
  • Login
  • -
  • Register
  • + {{--
  • Register
  • --}} @endif
  • About
  • diff --git a/public/.htaccess b/public/.htaccess index 1bc5c957..aaada2f0 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -6,4 +6,8 @@ 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 + + + + XSendFile On + diff --git a/public/flash/soundmanager/soundmanager2.swf b/public/flash/soundmanager/soundmanager2.swf new file mode 100644 index 0000000000000000000000000000000000000000..dc383ce7f4a4a4d991e81b1d8977fe6d032c5188 GIT binary patch literal 2910 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 I0BjL;aealQ0{{R3 literal 0 HcmV?d00001 diff --git a/public/flash/soundmanager/soundmanager2_debug.swf b/public/flash/soundmanager/soundmanager2_debug.swf new file mode 100644 index 0000000000000000000000000000000000000000..bfa601ae872a52be66071d5de045a32cad9782aa GIT binary patch literal 3284 zcmV;_3@h_PS5pYD9RL7$+O%0oa~szYZU8gHXtgZLf@6cqn29CP5kZ-fW1Au=Qj|&2 zA^{qpDKD|00p<}HX)uG#3`kLStSnCKET)qSt9(dRF8KjT<&r}VsmdjlKafMJa?LTn zAWCh(3F zdCzDGzf^O3j=5?$IPoUo(eWchv-a5^wO>O2R=s7r4a0^l;rW*9z{X_h^;eD3jsiB)B-x=Hw{~? zx~2fftGgS52R*&FOiqjZ(d_ww+o2^Nx;?)Q9Xh>@{uHb{ zoM(rRP3iFdxq7mG?gMEdAXDKvXuuocU>OB@n?{q&v+a~0U>#`}U9V#V;9Cy_Y@$vu z?^$?w+cJYTw1tKKfTkymKrpo>Y!mLo4s?2UV4<@Gw1W=PyPkQ=GrC}l%>ayE;MRyE z?a?_n40l~bfiHrT(7M>K;=1rr7a5fGovr{{z=-M+W#eaC!4>_@TB%e5PjuWsfbF7H z>mGUyoj2X^W~Upo9Kb2P7UED38S29GTyIKWasz!7Mla~%eiy+bOnp=`8`WJ;A5C){ zEfLFk)3gFi0K?WJZ0lX&bu6FK!q**vJOWp52)*SQXp70mYC`DKhTitX=FDh22)a`f z6WiO{rH+A_&=IC(l$vg5!tZsvt`|(0ZnKBM4h#}CA)E=0RM$3kgg4O^c6S23N6zr; z9f_lhmrLZ}(OITUbc^WpuHguKdcv61H_VRit9efk+Lo_(+${^Y9aC?%UDp@-ChCJ* zLZ^(F(uvvV5*H|pw3IojkI(9(^X8V}G%+ zfg_!3TOCV+vINcwbMphk!(tMkmUUL$mVT4IbrEShi)bn@vz}bG#>sS@Z!V;*_oUTz zSpp@Q{6zIPTpN=Cx(4PD(wJDQJa;GD?xFyJRF&Y>}5fN;>nZ0&cfAGG|3csX;-68=+GGaeRxj+Evx0GX5sPZS!TgS|sI%u`3D4KeF{nLf2@0r@If?a-2Bi9!x*N2clf zsCJksk1b8_ zEa+?HJr!8T>a@o3s#1-Z?a=vtg=&|Wua%4Ay(ns?lUMClrhNq zkh9&^ir5l1yvyM`DZ@7e9F|!b2wXUXaZsg##Opk1g5=V-W2{wxr0CKN3q4gl$=UrpaN_2)>&tTCaJ2BoGCO$`WcFo7=zCGgG3u5WZ)t|vVuHpS zNsc@gjiCa>zR0>QC#-PxW;{a(Oo3EKa=tl;Gag?_A75m1v{i+xh6KkdpDb7RoA8WO zNdsADG9wc9UuLB<)yyo1B&i#xS@|`|1zaN=k8r5-YmxxHl~=SIWJ!}z@>DTCq~yzG$@7rpiFK-0m0Be$ z>s6_mLX)@4`-+d$l=n8`lvJ_|9vxB_$1red%p&0}j?X(~38@OmGu_B6az zlpNk4M_Ji-mn6k|Wk%ck9ChWZ@T92MY4V*A#i499mc0?mzS$>x1JjJ=>f_~pFaT}n z`%nJE!M?qFlF~GXY1*junoVj}{{}JQ-ePPh8T4lC=M4HeHK|pJPg;%4|7?E5I*d>& zE~6mJs0Pu6_(U1?0j9E*cO;~-82u$?f2|MG*J9*kG^6}B&VX>dj2g{yS~VyUkyWEe zw)0h)wxvARgHt=-FL9^|P9K(RRoAL*6NO>AaCvHUgJNvn14 zzgOuehFaSV;LnM6N;XX82+u==XG+RSyo9vvxEX43W*h_RbMPV}D`ZZbg}vkQ%GE3h z0a?o=54)CWE~-R@FpQV#58;+}TsvFC;g``j9Jb$7Ho#flu> z)^0~bhq?eBxzy1ew8d?+z#S=gIyrLzigZP411P|1xK!gS_2gY7x~w z2l@M>&sT<`5SHcOc9s=WP(_T_undjti&#B{LlfzvkUprsnu*j`SpB|zR>$9c5g^B{ zBda5ysmlHraSBq_9#40hGC#9ak`az3>C7cS)q2&i7k^KY_Nm&I z3gO6!M=rHcr)hLcdq--qA;0eSwa^S#RhEkdnT^}B2t@AwU7w@rs0z`AoRe2ve`lP& z8{->US&Q#VCBC%zV!i^&m8_Im3M29iXChmz$V`;S?lWvus^!0Zl-48CaY}}1XSWbP zEGSHvP;Pe~`^7RyrBOy9iIHrNB_=N<%aPsTX4(~0!pZe4?ZNJ){t_mCZcv<6Kdxm5 zDwvtbF=q--u4bX2^!#8HIrnm*uj0BBHEJL3p6Sb>{i=Nam~`S;ZAqwJ1er@)ll6fCT(4ikjo`cleAswCXg{MB= ze`>?EpM0tpo_e_ZLUgJT{n(P@{~yG^m;6=YMi$M@lW}7)8TYUBWLL8Mz&cUnyPmI^ zJa>ZH)5Q?(NcR2k?eK&Cw*xx#1YYZJ72^g({$}yR>InMu21w2;vNMmE(rlco=#TnZ z_!GsLx8(8(B7%Qh7@3Z5@kW+jNzZ3X{7OO|Kknnvq4yj137seP6F-qT;e~YG0J@!! zZf5}9gOKjQ0J;xDx(^4?wL-eq0J@Jtx{n6XeH_w#Jb>T&Sm*!^*{5GTWpFQs9qcq$O%KvFu zzNJX)&k|ZKEH_#%)kOR}-ltxh%MNJ2pkkIO$jRrsN28}nr%Bf@|3pYi%?sS=gXxVIy)Vz$wZClWVw)$B$J75BV%NZ zT&dW-v1=m|N+!0L`9fANZPh0yGwFm*W5@L=V}qH|i>d3o>xeX`lrCkA zt=lK{M9PQ^W{eY&?e*yr*~TE;WS!5g4nyf&q8!&t`9ha2Nl4~QBqOIbP9Or4<#;At zOc{mhf+KmebX+gs%K36GSzoh`NYfGpgO=D@&K=9;kJ}j4J{r&HxrwqqVQd}PE5~YW z$+TWFw(ikSMmF|FBfUKvH`$y~@Ach_wD0ubhHhp3pEuA6kT1wN{#~(p!0*vI&)2 zKxNp=Xp=f_>F#u~Ls~{5r)PHM@N4P` z9jFofr^^eK(YRHw5No2K%2~r_6PNqfG&W(B_88fG;pD-h;cR@?NTzkS zB*}|e+cf%8dM;;V{38twFS^kE#%s#Rf@aUm871$*{kxscu$)PP8wIjR)N^>`f-XOK zRC1&JxMA9klO;o1WOHM;>`{%^P~B&LWS0*m+-K;=s3VB=!;Pe{J#Z7%T*Y1eN)h?e z&{hW1Idq3wB99UzN@$y=o+cBO+Ddv%IgFKLxgecr2xUzo0uhpX6HHI!@&%*T_UMjH z$qI?qENR7ZJIbbM6b8$gj60LhPZqtXqtYQggSrvO386|RZ5Jh##S;mhOEjS~>9ZL` z4tTDpN$-ODp5?_-K;J(R2B?39ugf?1=alXy-$kkF+|uMVTdEYLHyZ9TiC;b~*}oUKKT>{T-BYdCsZ z%@9@FGy@c6PuAq|45vCvt1t95J?pB#o`9r`9=)e z*(rs5zT}l2udpj;=DpI-;UO;+J&t2d`yAWfW8})ghWajCtf?`!L?9v@y1qmik9U?( ziff#lK~Lu6Mpq)A?WQo%+tYJRcf6d=U`$XZ%f*y0Q70X6;-TZ~ngy` zIZ!~wq6Z*bDH+SUE{q4U8x_RMgu2Lcj@0M-(#64ap;#hisc8Fn5`1xVU`6TRl3rOq zkjFTrW{liKDaCK?40VonjU7+n!6Oqv7wnc;PVSRFVHB2O7AIT9T{%6Gz)+ILP4U|& zd$**cP$>3HIsw8>LBh%&y_9mB=z0pCjA53@QEG)WhDNF_skxLcI%QoW z7&<(qyq!x0ZHk!M=?3j?m}-%7u4W=zB5F>iSsLDhyiG_j4A0P(kqm+8uFhUM0bS5Q6PmJQ=qsYX<5EV=4v7ws`uMMT)qgA zj1}AEL<#q2R}PIXom_f>0x=C~sdw5Qv1=eZSvtAwf^N#sJSd|y@C#=RW+;$uA33tO z&puvlEzqL2G_MG>L|URPH?Hus+~j7ID=c7%fMo)f3s@nbRlp?zRtdONz-r-MBj7UOzFa`NfGY%a2jhjX)C~fZh5L|z!vby= za74f@0&W#>n}AUPw+k2(FfKqB5EqaTkQA6wfGJ=?KuSPbz)=Cm1Y`tc1>^+e1xyO< zH3AAkD+(wHQ5G;I;JAPj0!|8et$^1Fc)fr>6YvHBZxnC}g!c{*FpY30!d(cb5#EIG zW`wsOycOYX2zMjggYb5QcYyF+Cg7bQ{O`i%yAj@ta0cOhAe!z+_yE>Fgz#Yyff@Wh z1R{7AzmFh%1O$H+zmFk&6yakCA4igpBRm0Z*!nEiK8NrrtbZP~=K!=XAbb%a3EC)V z$3S}?K!~p(!k;6&fbbl`7ZAP*+Sd^A1ssPjB76hkYoHy)&o>dih45{J?|}AgMEefH z_Ye+)b_98?Ap8KdZ{TF#Um^T8!b=E0MEDWH-yr-L0T=ZB1mSNHZ~@=nA^a5KX9$0f z@DB*cnD6HZ|A_DlgkK{33gMp+{u$w45dIb6-$0uJtrN6=Czk&MwBOL`Xaz50^LGfp z$L4>6b_-~?f_58d$3Z&@+G|0};^2QH{0}1BE`0xspFbe{5n&$T6c@fb5DEf*L)*-? z3D8oYjiZov;=#HL;WXFcwB=kojas;iR(>;1c?%c*x8m^IxCnd?v;Y_UZZ2H+AiN#n z9XNC!!n+XO&9(P%mv)BZIej13beA@YwFkKNLG1ky_WB;-Z7wZ|m5(B2Jk-9ABRr1q z1Wtbn;c1RrhLb*t^-po_ojCDcBKK*IJAvOXa?Qgx%hxfGB?sVCau=2^t}qXP3tTS7 z2?5Z#Fc+xU_Hr-&*`2}}k%isG8RwvS7*l{Nh>l$vW9W6b%SCN_oq%j(k3vW`T5Wm7 zg-f_<%pjd=NlS2W`6LsZc&q&dnBO7kQ1mqU@z3l0xm~$?{TJu9jtlG1c#1gUa>$FQYk^GuH0+U%^-h>y#L4 zWw{QRYdtXKN?`6NFm(ek(FM%Y4NU6+=G_R)*9*+Q30Turz=Br;3ta=O`C4F0t^>Ao zGq7b_fVI2|*z)Uvt+)Z$%B{d!Uk$8n8?aT|fnBQZ&^&C-);`8i=MA#-Gd92m85?3d z8QaB%8M~3~CSLbYZ7*im+j2xoAX;>yyl`E#<& z%W0FqE<-I|{u*Fc6o7RUfpwOEtt|svHwA3{abQ=T02Vz7Y{P4Tb-fN)_v?Z6{28!~ zZvfW&Mqryx0kEs?z~bs@EUvi|i)$H+Fuof6XjRvNsxa7$mgCV}R?iUH5C^X}Fn$-r zP6Hc_cEqB9y=dg#;1h^(c>Rk#21HJ^WnGqKE{)y`GpS|PA(m>fMpq86WT_R@xXc>u z817)HE3942GQ(5r!+2oVS>}?dFz(j)n*gh^Hv=Gv=IDEnL^Iaj0_I!6d>f?J7%+M_ z4Brjx)ZLH;Y~2IV2XIbkCi+2`!8tR-_W+!_2eRCN4DZz6jz}3W-$5T9dndr{_sX8s zy)b?snD2t{w{Y3}AiB*muZ_JMIMugX=5^J2zh%CvS|6~?>#Oxa%e_nK z=GJO`r)9pnTHj@v+p6`eEi+oJUt^gYs`X*Z++MBku*}WXdY@%(sn$1H=6b4!@Bmvq zvpu)4_2<_2T$zPc9u#@Bt$k@b8*byY4JfB;{5@d47q&s{43I`#u`(NiFTt<*%lv)t zesJ9n4C6oh05E$V|twO0hZja`F5(S8y66f04`F562 z5ZsZ|33b+d5U{sm&Oq^e#e4{g38i8_48`@$DvD4s&r%ir^SX9cOt>v`S@cUVQ!yWb zndp~cCZQtNadj@P#NEiTGKU8MgI)pMk7jl4~-cGaH~HNJR)Y|1lW+D6pw)vjH91F!e5> zVkzzKnf1*cd+))FH#QDUH8c*M31bu+GQd%&?A$L^N(*A+Sa>yOvNq9dquLGgV^Acn zoz};(rPNxFV+&;*^~&~<>GjQu+_~vNFOH}+yeHs_UxUMj!R{CB z?CJI4kj=5E4dOLFdrXxZ2L1svSyV!u|OAu;w(|8DTmtH?_hUn{0=^ z8ch{zP1R|Rm>#rSw>+>5w(sjs^$0E(#_XjXrbBpka09A`-0F!d($w9Ksk=j)91(1W zRhtcrA8pblTx{`>hgbKk-?mj*Z?uPn*6v#j!ie1ac#%Z(4VMV3YZ7}KU(O*Kdmn+gxgnVPfGvkMQ3ravoJLa&zbIw+r8h~m{hS4HW} zE^{nJZ(QUs>a3&MIEJ2XWvzi|{%Ru|{Vpndiri#|(xYYefPWg@p;ntQJry1Yd-Z+c z+aw>&jeBxSW8>B{;mtOyu_6m~RSy+Z50yOwuL6uDAm(`n20srB0|y>BIegRqWtCFH zD*XSl%5F!w&%u(V4a&s<{W+=Lplt1R(7v!3ZLj_XiH1QZvutVsr{NyLT;zYgPvOHc zlIUO1r)7pbkL}BDiT))-Eou6%a=Z-gLb$t1w12uGLLHl)wo?E~MjGm_Y22$IT)6hE z9lEd?B-dq&+0L#w#RtC#JUR!1UjjHC!n7)KT83?jxw49B14St;of+KV*a8a+p!wi+60SgoFd z@RF(mpRNYfFH_7MT(GZCgZ^c>K!fKScF#e(Fo+Dfey_aX{)U@UqkH}D^FQFH+VA{- z@ZZrywYKLm5meb%FcHLy)t`gBZNGrGR4@A~-c!BoYj{)jvajP^)yuwtw^c9uCgxZQ z`xf3<74~hsvkLYdytNLn@8Z3cv+n^5xY+mc{_0|Xfj3we`%ApTx|jtl>|(QcmvylU z-ez5F4)3!r_5-}pa`qzLX%%)3Z?&AA$9pYje}y+&&i)$jwj3A2JUeRjw6hm2vjrnV z@mvTkU&Fi63hbcN9RJ0-;RTqjEc^NVJZ7;z7iRI7SUvTqJ1etLjJfShY6kDfD$;!` zX1Y#VOY|sLwTV^Cm!KIhqm>T*hfpU|OJ*qvrWBa+-|2e(OKMjZqko+$55rU)ZR!>F zim@gIbMO0|+&gk=`FVtdf||o+X8em9Af+@cnmnh!@yM*qHCRf0c=);*7RFwxB;R+mkBm1cAV(bw}LSrzsJH? z$@T<7M$c3ztHT_!A~O+tpUdTQgiwk79p3SAo4O(N9yzDqq17MzDM*^t>`B^-Ofofl zv3Afug{xH@q-x#;aV1uMr46)~v1VgefinsXqjUqKC1~lXMjnRveef}OQr@XvfN#Qg z?b}tDbFbGk{xg`3y@9U4OM+f+@B@mxZF!t6@z~cQ?*$jDxI)Qh+)Zg_T&0XMF0vx} zUleARx@mtp31PujQu6L`t~8yL!(}|C-m!Eg$T&)QpBNU*UQT8w9&^9`2sjOQHHn33 z)LIQArvH7tu}}YpdgCVj=k>-h{iS*%qyJ;Qk<))sZyb^NoYTnbzm$y*{a0Yw?8LUn zn%9(Uaapz3H|>>8dri|`&9v7sZGKIAAyY1)$m*}a$t}I+FCn%$uvlYqS=@75SIshQ z&Uk9)*nSG3_CH~}m)g!%!y_$6b8JI%u1;JZv9-}0bSP=&Kf|_gm5_XXI1Y6h&_H8| zKj?bi^;H)c&re);DsNV(=CzO8zsTo@f2E+q{taV}i~T#s9EJTFV~&gc2gV#1`x(X@ zmHh@|j*I;kV~$`;gQ^y|)75w;HN z+oXG#fltG;wxd`U3~Anb(Q}Tf)@(&S-o#Z5QCi#^_r?A3rg&gj4V+RFL5Cvck`&A- zSW#Czs6T-G2K7ihIIHiE2P^tWJcv6)#p-x49?-MAf8`y_l8p0l@RIlXEgq8vj#U!iBW@y8(cKS=!5BwX#KQ)*&PUeCzv=gf&I zLdI|Ge-SxARVnhEE%F@h4eECVrZ7#XP`{lCbwSj`#Y6 zLUibdQP^;=l#nhEbI3>S@e3KM3ht+&+vGcNXY2%oT5Uxvac1N z7^gU4TmsB~#%}BsSVc9$!AQ zr&9g0J2<1?0H-JB}MbG$~dC&M;5Z;RLHiRJlyP2($eou&jzF z%S>A4YxH+;St;o6-2u3sey~x9_*A&n`DLTezM)uj(|tdmL5E9E zqB|D-!hrEaQ>EHL_z(}K`i zY(4C>IC-$>S*PV9|3*)w?Fv0tv@0H0+tmEL68Pi%5B@msouA(}KacGpSpF_JBYhH_ z!Q3+)+RySJUDD*={n$GG~;RpFR?-Q~la zVLt|75@Q;}ZxmHQSBJSvRA- zrT;!+nX8+79OJ5M^vm@9ICedbK)-<09vR=yr|rj5x^2_#MShhA&ekpDEaR&F$Xb2Z zb$FB4Z)D4|*;>TGV^R*9tI!o3kAs~FCAYQX0UBcN`%%4ueSrRh4)y?NuI225oGHuM zhd6U{)}g4XIEumi^tm>ejC>I>QClNRL2`PvDM$xI@M&F43`Z2^D?rdkV4|5kfQGK_3pBcuqsa-i+ zJQuoDCQiFYz1TVUAZHB^^Q02_So0(KShKc~Z?dI&`XQnXvhUF$WZp}PQe#x?fw}p2q=__>| zsT~e=GuhSZ;E%)}Lhsh8-(Euq$YpP@kH!^B{$x8xE6>_51bEzL%kpKw9G}%^xD|UH z3*AhJ{b%f3{@hd;6G96U>cNLN*ZVE`h+&yi(xVglI*D$FTbY3T31myYK&n#m{>T&L zgF8b&SsG*OU7(5(p61%5m*wVPi94m`>69M}Z;(nr&FURXRxXgzqQatD$ujdqL)4w zeTG*YFJ1M$Abl@*c-u?!^YG&Q{7cfPdx@*I#z#%d`5m$Lrnw1tb;+yTxa3DMjCY=! zwF5%*GyL)O&GchSwd1pkJ09YXhp#4;+HF01u`o1i$DrAI02->&_%1BnMrybj%_}I) zi+=F2LwxQ`I6`B6n0+GYn-`I;J`)Dm2YI~b&>&AQ?UI^SP@j!mGb|Kh-|854l0jPAZUqb(}--Z8UFw4eosuNIHS65wk)pd83)?M#||Myk(OeP`t z|9$?Se?F;sb-a3Yz4z+qs<&QJKa`~WTP0~MljawWlO*Yk5$`ZbIwT%68#GFfJk|g9m#NLPUBjmtv#d%!lCtg z<3M(fEHDT+I8e_yb}KugZJn(~G8Qj0I0;@k6J&VS>S5)_#=@PEP@=p!7GI-RmZSP>lCibr2`VIF7gC;xw;g=& zKDo<`gd^Dz3B?`pP$HBZy(+Xm8Hz`Z@Zu<-X|#o!LW!i>73&C&*e7#QMGohnvd&~j zIN@j_I(Z@{*pXNpHoBcF*2X)MA#baZ2rXGLFBVP4W8rWpu9fl1C2~c=ver{WZAqty z^UUvPBThBq-MKtQq$XTwVgenO@HC;40nXVTQCboaZu(h~><)*<7~ybi-KzD$Sj6av zuCgU5x>~biTq{Ctov2oKuE?F}P9#H-9G+`F&*SL|#S^H)DkCvQpQ*RSV~NB}K~4`v zjn;4|sK#Tlq$eCRf}!~0s2TIDHR47jgicGix%bG9MA8uqwRWx^vrosN*NdtvCfgEY z1Y@nCvbI>H9E?z2RZ&q}-rCs_PIg2`4(iMX(V0bqBj<~r7M(;rj5dej2M$K!qiLX9 zyFvnS!r=5lL?()~P#5Wvh6MyD-AF z4DrqZA_O=d>*gU|L}O<%b~J?3u^=9g#Titsku<^$!?Yzt2o1U`8C#Kzqtt`#gMNsI%nJABlko=WC9Bqv;^y2{(&84w zTiT$j#NNYHdH~zGv@_BgijN(BCc%`^kqC6e6G@VYoP-e#w#L>sb*~K-Vw_fp;88-+ z=Z0e_ttk#^4h5ZYuy-^kyIft~)u^BO~$#t8MZgcEHaNd;j)G2;Fw0<_WyCpC=5V$cs9PFOUu#4|;X}%eIXV`55V`2gMNLgdqv5N|{JPl@ z4q@e~L%Wn6Qr?Q5i>KFVv>N^YyOv^b;&YCVb?Ol;u#9km_ZTTE>byA31T)*P7G0w! z+dC3^B-Ygt(lP3KTYD^)2iI>g5oSz4yfWTy%X$9qMRhed6&wv;mxNoJX1<=w$C+D;oo#;$X za{4f`4o$P~I<8>QtTDaxz+p0G&nqJ^-cLDjm_J+}b2Z=WB_hLdo`6aG053A4(&645u;B6c0wSEi{kX zn8&>fqF9 zFiG?v^*4I3+>TANk613-RfV2CoEHE@M>rN+n^2E3lI;!?{&)`Ra&*X8Gaoa*D;zSD z6mWC#Bwt}$EFMRtiQ5yji_iJ9GYzvODGZdx?P@cwk-002; zotE(zh=Cy5e4g=3%i%K-1`ynmOI<-YN1~p2>A-T*;2zN@lwfxck1pckkiDxd z-&g8b2RqfbZ}c#lh$E$ib<)1HV^L}gulLyfrE0P0;`!A@603 z_9{gco+wb3nI1Y%M7#vok8r3c}dVV{Jxp_5>ExgPnZQA*S1K zN2Eh6yUM0!$n3w@efxIa%qU)j%P^{e8ECHI3ar#qf<21aCDiyEDO=%Mh}K?h=C1h4ki;E z8BjYy!-y0o<+eA}MHRDb9~3$c5Tk>~hXi++swNz5VJtgmlx6ZZ=u5H9$kquQLl@I_ zgFZ?qfy_kChbWJlYG*W*#0ds12e3XZM@S5?&Wqq%JQ~!z zGI2&g-5pyqWV^>Vaw0tsr&w|Hw^)!3$8cHa09g8EodaMQhx0kiLkk8)Q;DKMSh}Oz zgWbAlZcr@DGc~5V547a4sryhF+5-*xFdBYPE*H2xwze@G)=1m|9eg!3+_)2hOB|hG zKN&p=nnLIvWW}L@KHcpHYe7&Nbnk$WinOUBAGI_swF6pJ7i%``H4{)U+bcB>26KTWD(^;iESBRt5B<&HzdDU=ifmt|nm{o9WRat$VQAX$V6*ZMrV^*zN9b1)* zfp=)?Btcq58)pS;Q?;_Xx+ZTriHK18^`(fKU8f>*Et>&QS1s1a_98{Cs;on5RdtP8 zRb5?ypV{?l)vWqjwW_ANTCJ+BK|)WZp5{M61;t1Bz&@l%Un zRb{s5noqXQ&+%6ALD8t_2!WmYe=DA z3OS=>pCOMPln!+uj-Dr`N2m>7Xoe0Gx~Z;8MK4v2a1C{>d_pz znv$m}1)Ad3)Iv@1Y03!AIZ{(bYswf+8LMgIG-ZOOOw^PEH03}|(KTiA1g0r7H02;o z@oUOVO)1lqa!skwlqyY`r4`m_g>{-zuPF_h`(RBuMDrc0DTisw;hHi}Q|4>R0!;~M z%0f+9q$!IvWr?O7p(#gd%2ArKR8y8|%F&u~jHWDiH!*F*v6^z6rW~&+Cus7CntYNb zw`fXGQ$m_zYRYO&Y1f<`n)6gmS)(aoO^IkqR8wM_vQ|@0)7%M7Noq=`rmWMHUunv( zHRZRO(t`$^1-B9I9Jnjsu4I~gHr!_TuZ6pgxvyu;eFIz{+*Y{t%zZP!EevSOwGp=g zY=gTU*mnSKhr1K*E~brq0PaD=KLqzM+#_(0!mZbo#{hm0_c+}3a5unhh5G~C6L2>( z_me2)ag?Gw1@|mmAKY_rPcrxO6#oL;i*PT&y$tsX+-uDJGV;Ab>951R0oM<=18yhW zn{d0}cEi2R+;0=!UzqzZfbI{N`#C`QUby?6&s2&~BC8FM)bL+%CAQK~cF+cHc+v ze%XD$%-j!Ci4TCpgK*p69)^1aB;c2Chr0uAJKUXccftLh9P(@g-ji@o!#xA{JmR|D z-S~Y@c0UKoj}qk(R>RBbL zP0hdQS>G);H=VG7ZDve4{S4OJd==|Gn@Q^Ln8egm?`FNr6?J8E^VjTL*@M4r>|S;s zlE0_qQ+{AQ+ZfP(WIf9P&XIeUqYR>dvD~{}R#!g9E|Y<>TveM+Xl}k4ky|htR{`TS zCig7Y)TXB9W~4lhlx^tPJJ6|jqC-L91w`fZbYPr7*_)eRl6!#m66(tIbW3Nb{c&w;jEDl}rtO7a{ID;eFuzmEunL5WWv& z>KtU*%l;!%Q)T5N^t05ohq_Fb%D<4=m-2sQ4!#oLYXQC$;Clgn5a34veiGnk0rm>e zqi`x`3eYRSSpsYjV50z=1UOrO%>tYwz_|jPC&2jvTp+-O0$eP>B?4S3z~us5A;1;^ zt`y*E0e&aIH3D2Kz;yy#FTf1~^a-$4fExw4Nr0OLxJ9``LDy{;p*t0}Np`wDk~~wN zD>Yd%W6}tC{s#{$m@74VJWTc?;u}OL8a&^@qq^Xcn8Q6+dK8T3%$X~BB&Pky=1Pv- zT;}=#9?I!H4lpN&dB%xw&RKRs?&t8V1Zv)xxzZ_s`MH8r!NR%HB80v9bEQ0q6`m+E z`dS1WF}YDv*vQ$9lFCM%)F?UFXr)ndvN5w7C5??eOKy~0Y}`YvQF62K-vfBqgbj$y zVH5w1P%fMFI6xjd;3a?pcHl;&d6{0(C>65FGa984Y>K~88p)>CHcF#dQC*`nnoXP8 zD2-voWsSJ;Qc~V1jbqa*8>R8Aw5m~>z-Cl8N|V?@HI32%%#V8v2ckdcgF|PQ=@Mfu zx00$@h-jI4JQKc9taDilGg&~EO0Dx)i&7j=SgCa(1B&cWJO#HpfU`7J(LCd=iapFF zEe$cf#}QEoI3uccNGfG&{`|gnmvva`SgGA@%}uphNr^IV@a){x};`kz|NNSm2PB16( z-${W3)KY6z%Yl>{I1nYZrTmpCQ}6Rv_o2wXz+}X&2B9fSrbsV5)z17S58X ztY>OOwmLvWE-{L%Q{iFeGztcak$=s}B^1+MVnte}o72rw-()6P(UgBKC%-o3Z{(iS zDAj*>%A656NG`SFJUBmP`gt(Hg9}pTOdd?~U?63d@n9zpE=-x_JlMs9i&AC<53b|E z#VNCr2iNo9l9XA+gWWuMM9Qq@!P9xrnKBg~{1p!#nKEbb;IDb`sFYd5gTLXyr75$P z2Y<_h%Ti_?51zq;7~k1E*u%V2B`H+)LR) z_PYG2`jlGVpw5-p6sb{?r{W5a;*buPRHvjU4!7q%8M!WJsfvv&=ylNb<*7VGS6Q?2Wfx=+#SHX_w6Nkxt{tKAU46jp|6_0EH+8ao}ka2!ZWoms9gBsBT z$3vX^k$$mA-vRF>-0Pi;oNr~&FJ*mH0LR}2q{{^UpP6?B$;}Ruo83Zg-X^)ZoMj7+ z^%vH1l7CAku1F4?go58;EiGot7M}}6y~|E+DQ<7E-b3e3aw*ma=tj2wefBY{Z;(%x zShKWBlA9zAt4PlzbnD;It=31V$<-`y5{B<%mMUpK&iaH#(d9tr|A{*P(@f`I!`P?j z6TRn@h#X=?5={&ERr#+22^%9$du*0VpEB#;^dI8AXd0Bs`i}_8W@`d+{#iQImcZ2V zc{&tK%;+jad-&LyAqqjjN{ggKBI2<204-&@{5Oi;G|fK$O{@I-BFD_t{um`RANO6=hke!`T^)n+@f#0+%fmxPQM%3`!z|J^^?ub8)J=+J#l z!*pwAn5biEEWTw6*&)}-H_P=6&JKy4Dy@;^Q>3t@M5L&sMx>bJh)8QCXOraiI6Va~ zj6u(T&wIX21>fJplfz(v8F5=@+CGof3m>sUj&&CI zofgTpHjr;)B+uFiPf1rK-`XTR1=iWZIigl5^(7F_v2l#}U>uf&Bvo7ak&Go*! zWy!iy1iH8Ij8`G!wTR(Gb(^OrA};H?f$*=W&#oT`|C$)`283H8JjZW(GrZaxh_D2h zvTp$Ux6}Tq-I$1I){TfmQ_k@DP}kei2tCX^6InFt4ocRFqo+2$+c+p?uttw(iNob{Zk;-Zb+4lt7Zgky|gC7ep!b2Ka&xE zp`|Ce9Nt4wp+633qOFZabomZM>Z`w4NAG>$)_xE`rJ1>b^^m;nNQXIR?o5X`U%rWu z&6gY5zKr=+8hI1rLcAN{aBi3HUDV{yA}!ZEn+?*wn)NneaZcIHiWnc5{A>>0h&W#z zNW25YmUEcN*K&e;H?o@o=Q7qA8DqUG3P{I;=9n9|67Rn!=%2@Yhk(fYM1({u*F2w* z^gyTpZX!kW!9dytgVQ|Tk;wZY(u*X*{JZV*@P+3mJVa&v3%;=8YXtIt$V;_x=Hjxq z^_iUV|50=_)W)lv541l2pSZ`_XHUJKBNJsIT3@CK@V+_IUT@ee30c0AuRrWCGcSZz z<3jvj%13#qd=ldSA%Y9>Hu#A?uT@VKa zAMyGf3Y5-U8KuJ)b9Vs+9XHCl8yDu}16_83*|&*=*E@v8j+D7}N zS+8eU-Ty8XH@c6{1#j?@iVf^9-&Fj6W&ZbU4KP>m$Oz&QUv-+hHgJ8MS;VaGCG~7i z26A>l=><2<1~={Aw?^-a8vPk5s7L|rW&o*kilk{ha^N~9`~SwU9o*=}k)^(IV`QxL zb{ZK=0vlPNvLUGBtiK3UZ-(wQ`=vYHdMAyXrh5$=p6&$eUEYHLD4!-{(z^Lni2r3~ z{gqS!!|MFdS-o!xFLM-57F!Y%J2cFzljaA zK=@2qsJ!Ov({9u18@?~tN-NmGl&C=aW8r3Y_!=%wo(!ER?bAp7U`>{>Z{Y)Cy zgsr|bzDatJNfVkRkBUwFQtUzY;QVk8`vxqC&BV8iS&C&T*gU+IGTr7zme}Qm&U9N} zC^$#RK^)Eo%&~!o7`Bs^(*Jbgt^OUO-rKv^`mcgbWsV}dJ!PQ!6{m_Sf2CO8@UFDG zxu5oPSa=mlT&}*QT)9rv&f$tuH%eMqO%+?)d+YU2GLI`Fn-C>5nJUE8v-3J{t10v~r z8?kGcao?b#(s`=GQ*aCIpEs%L!;7C7+k{El-aqR-kFp3Dj6;gv7{R2>Pd1iIn^fyU z`nTsUG!eV!ixE6l;@PZAGJe&%R4tNrgAZl%G1l9rdMhy^A=awyJOCuE%YbQbh3UNV zatac!6E=VY9Y*n&s#U5|_`=@3E*0C=E3=!{DI?@QtKMkTQYR}d#wt6 zPn$&0N0wfht9;r4Ur!n3mQ&4B&EG?+9%q3+FdSgqpe|$$@3Oz+jKVRnF@6Fffs_3w z@ns3%DJD6ZqD4yJIv~FvLVJ?)}R`ins^tgf1T~?QZ5tB?g*(3-=<0kHRx-ZoYlE z`UkMA{4~C#G|{=lO3AB0B74{YIB0+DLqFo?wms<)gkD;<3E>J*#TzY&{nwL6EHJbiG_ zncQjpJ*$z{M?Am+FH!S9R*BcK83sKpQ*C;m5WSmtMJKtO)~8fCoWf<8)WMmQy5~Ql z85@}Bo;~V9`Ap?@Iqkx+rLH#+Zea?4UaZ2!Flv*aMyPTVhQcGz6UFavz(T~9y4BN*4}}L%b4*7 z=1|%>3QAWpBZY4~0}<$*c_n5x>DMcH=sC1wXqqv!eJb1hz7~Es>GkTBt1_`XkObPI?0Q~~c_a&t56d;5EZ<4u- zB4~r{G2dc=+Wb=M9F#kFFwPgni}lQfR4o^VV2keS+!%8-q>nRSgQZT%M7XIj?^vac}{|9@d~eYWE6 z%j5~XLwvr~VaqiNf*jff-9!b^xg34ocxk0(+k0Hx-iNrIAbr3jXS4J-tWmiDx>iabGRfU6{T*x6Q>2jvs%OOaB!}A^ z>DA94WVQ{pwP&$y4(oP=vbr0KDmvzeAo3aQ4r-a4mV@mGQP_N&ZN!&@yBsb55R;g_ zl=ufNCGO6|+Fj3M?{Ux)b@2C6(J(vy(we*Mxij#!2 zS~kxT`}oYni_L~F7i0D)#}0f%`u_J0Z>e1gmj1}|2UG%WV*aOwj)-DCBT8EDI}(JR z7lb}0LNADfZb9}%5&DFQ;41@7mD8oXN=)700IBdr!x>kZ0J;vjRikQxa7lTRo8 z0}(==j;>R{(Ugir@>4n%Nu@YXqpyLU-2V-5mW%T5Bg zS4QAI*+pP0?uh|(d+7IJmP6n~;kOyB^Rmpo1-sZ<=Ua#MJwllQ z>{&TP>-<2hmC-ssAw(a_z(w|UH==qErv2{E)DJ*aOgfWbBk5Ah-gI0M3+y3%)Z_Hh zCl8_$R-VS<>mZ^eK245#44dM!X!;Q=by@^%NC$tR`_4hyxjYcyI^cZgz8dWU zC&c`Z88N>|_+D zc}QrV>zrPEQss&^t$k?Uw-qRS2V>ocYbSiuqJw8{N(W9ITWa0xT&TRsK470fWG<2K zmLGzs{9FD;{&}d#SP+?r^aVsFB8{QD3Rlp6@K$H40!8d5vANZ`hrK4VTb51kp=T37yEm0kqi*}mbV)cGEV!H$X zB?>gs_aI0Md(!gva=#d{`<*+9*&lR*8OJ)XCBo%D#d;XEYsUE~q`F^LA4YeS*x$*A z+ZR2I`fryRfgQ3;0Q;hcoz^4Bm&Lc(*gr;nFZM;ni>%)}Q-1mYMEe@CqfltKF5@c= z?PC1&ISA7~|Agh2d&TDMaZUo;gc9R__!O585oYfUJ{{wu7p)7Q5J^~#(5CCjEJTGi zT~G6f5(DR-&%%rRoac!S9Bnze@GbN?=Wv^j=ZV+oYZTRdm`%q^PFe!m`;3>J!|gL( zAriZ6=671JQf-`zaGUr%`BHGuGxF>5TYyfXe6fI$Bhpu#ld^XZU-Ko+I6An&#Z1L| zosZjY4BOkX{5oi+cLl^|uk(RnuRE<2QnCj|vHGceCCyPgdB{9ti|;mc-CI0V3@+I% z;@9Jh4qx0v0PIE|Sl$*Peio@ zq!O5& zUfOJ)Nkh+ld_I-=Ui0(p0o>#C;ygc9v6C-^Rk0AZX#C!>#}E4?=R)>J++UIsKEezg#n_m_zGk3SDAg~w7#~5T1f3Tc4$Wc2bZPRcO;e*(wTp- zGyj0hAIR(nr}fjI%s*!`e@B^nG-RI0aEr1KC2!VRzGng4dB&!J5+ z`Tr^8*8eFM7n4zuOFpMBk}z9b-9W5#^78YN!R5_=B)F0ko1~nmw-ho zZ7j}55&x1oh8YTaP^B^2c|BSBRi+3OgFkBlNizZ%*CR&$iEj_Y{|1;%4 zuPkAfIS}`9UGq{Ez$k4wTW&eUJf)&#vutjbD_YKx&2s?GmCbVj&Xdja0Gy_i{~RYx z;(zU%Na{+^ollLte+KTX;-dB77qVFvS0pmcg{yn`ApeX!A z{dQlNm>4jv(8X=0DX^X*j3W$V1tHsl!XRS}*_JCfYdCm~Wh~J!BK+;H2Sc(vbQ)}1 zB;BIk=w=UW^`#>-787hE4+C~0oZ`8lOS}vZ4KbxK$cjYPo9fd2@@Ruj)^O66I2!a^ zzfQ)B$T~oSo@H^QkUvIT78Ps|=nRK#Qy+3$TOK%O;JqhayE|VA7yWWp>5O zR&=(JH5*D5?N8fmp}xlBcy5?;;_!Lr(QEqPei8%RAkK{aOJ_8#Ol)rBW+z!9W20<{ zjf*TXDjzVM!ON5R?c8qL)JYWV)Y=HACJX}+p*2uqMJy3+4+3 zrGkhCxBdY`l|p#((jH+Mgb?K?x+#zXgkU-c8CF} z?2Z{Mzhr$S%Z5?G5L-C5*(Nfv10F>cKAw->vWMc(8Dv7nmmqyPC`zRq<86B$ z!umyq1{q5D1zySEP1}DB!6Rv-C>j|?fE9*cpqMd?z)daazepBF81i{@G6V1n(lvOb zrtF_^EP8nJ{8%(JE*7PQ*9vYHODAKBFe|vOBiXK_Tfs0e(gdS@WD+P8oIGZT3QimU zDgLE$2FPdG4f}<@8Qu_0t^u3WZQMYOZ6@d!3Jt~@GuQ>jmGG_8wAcEF&Pz1R`hsD?0CGWuxPJ`$C(l@V+dg~KkWa4z!0nEBeb z=s*}oYq>?Z6^qFR8qv$HmEIbHeHB`ItGnOzH%`bwbG*UVOHX4O=v^|LFh)%yCn zT1O={qhQFQz)@LMRZ}%!e-Gihx@rfXmK^Adni>bpr_|IB*oZ=Sc3o|@FNlaCQs1hiJCk~lS?#Zx+a%u@(fKr zNR#~BoG`U8TYc)Anlk+sWK$E?iT&Kx}n%tnt z2W#>nntZ6{n4`&uY4Tjn*{I3$G{JHTft_KE}PAX^!JGdBA*$ zy9HG}*}V$Cr)aXFd4ig}T9ex~xx-z}+-sQoAm)xTcamwYWt#kJP3~nAm^%;nXT!ld zi42P+?sJh0D<&>9+;sumg>V$#rY!1@sXFiueymH&bD`q`q2 zCsw$@uMU~I69vp8kdXBO_Pk2Qyvz}wvqSzl%P1N`S8g_(p(l1^7+?JOLz396b}j2{M771o&Biy#n;$ z8w%3S6rfjtvjo^6z(xT!32?Rmn*}&mfb#{oK!6JcxJZDD1-L|jO9i-0fXfBALVzs- zTq(d+0$eS??*zC;fNKS~PJrtLxIutE0k#TolK{5}aGL%Kr$hp!8*jMS8E453^;hZZuVDv(pD=mf53l|T$+&tv=&Xp>_(w;&Ab9@5k z=5u;^1svp$Laf9JMswhW2@He^$8z91iGva2I2bu;u2djX<(g6Y<03#b{?Sa#Hs$MczDwf#fnz_;}w0%n5T&Y%K zQzrmdVnsSADGHl59yLa4@k9YjCJR#24{DTTRyw5-<~U}+9EXD))X*q7ng0wj#4)pn zks*$BS1czWso(FnoS6JS?$>Xg{O>nAcG8<96W3KQ~ z9cN9(C41%{(bq0pQ&KI;5=ClPt!elkw8SB`JFQ|ez@b?sc7)5CP7!YF;FSLW>kwR- zIM6y2zj@Xi{OZDy5WnNBP)bLWO#F_wR;OCE_6b&d zs@2^-(dtOGdfF#hr>0tS+YhkTq+0XZ546IqF8)Q!ilkay?Yb39wdS`^woXg=XSaI# z{PlgUIemdC_-dAbr3T+ReDCT^nG@5W_fzJ9UGyT{>Vge`saAKYrD#c!)IQBRJ=IdY zq*#JYj~;y5ElC;E$zFw7(o$-cnln=7K>cPk@Q&?L7(&g zm6=s$H7D+c^%gf7q-dFiLYB;8MvZk1@M}`$WMeWnK>@FB=wxrArFKcJWYodlM9Q27 zQyoS<8J!?+gK;o7Isx2iXyA=Rp*U~|ItwNz0*B%&APi0v8)LYUiL5?5pvyN4^|}r9 zYUO_mB&u{kOM$BR8vF*&e(!L>*F?Db!6>>NX zQM63)pUN+s%!8#68ne4Or54#HG{4Uu?(>U>)>Y<$be9AUQ~3k?dzlxrubT{46ytgc z3|07JoDALr@Cs$B11f9n+=HmVj)nIGBJq?U@$@hmPa8JlGbk+13wxG%#}J8E1PNG| z@YAJh8oYpL)vLo$9kx-{pP2Vmx@+`TLF+xx>P)w~&)-G2JF@7$KMY-dsRBh0ZsrHf zn~)@L41)-B5;kY;_OFA@lbz!s4$~xhtn6QJ-EW5(J%{jj!+wWIm+2F}|8$t%5MjlO z3nnEbNz+t%Yc>rpNG2@ayH{QH)82JI@BRJHdw=?QFTyZ5v5?LAl6}YKHp0dPD}|vJ zSQ0!ClJF5EVGsL_^bw2{{FZ&h$QFf}*S{k$MaHLPRlzX7zMHAV&AD~Io;`NDPK}9(G!`^aLbDx(D2tYWm&Ih zi=F`OPJL{tc`Tj{+$>J)L>kemY}eoHkOwI>dl~$@rIk^9Vd5xY3vd zdlD&g^cEl8rTS**m~A49BCy5-3p|tP?wboA>#1Jm5xvCc)_0(b7eGsK1(wV~aIXq` z4*RV>8F&z{eFDo^DYx>#?|V`-+Q1`!7*zcpd5E~2#~V!-*TmC-UKxsR3%#1eI6!yI zRgHQK!X}J~<7iCW(lL@;E-jaorP2yXT?#v0j&jMVI-T0hbU|XXjJsMJcTMiaZ7mE7 zR*L6>3JuFDx|?+o!=;@)?0oFWWLSa_L$u2}AFm(U)c)Q!2l1X8*A>+RdUwM@#EzXb zD*Zb%<3YGp4z=)Y42Q#|!WIOXW7#3b;%d8bS;`4kS^8m$1${vp>KfuKO$ArRQP=BG zSG;3k(pFSy3u9O{-XOQEG*_lfX9=FD9gCL~eKIClL$kJ`hV&}VX*L(BH{o5}G?LvL z(Kf9GrmUQS<0WE%z;X0g{U+Y=TV?x!+*^>1o2g`1y5w8aCEp=R#$t4vU9zj#F8Ov| zvK#gyM9Ho}C2vQ`ZYmiT4)!Vej_i`}qLSUT@423qEBB!25;B)`J(KQ1>9Al)ppRjb zW!)!3yB=UY0OAvTO=LM~D^s43t%uOdH^P7q*<#tz=f9cwxP~lk-6DJ^^uZ-QjJPfJh0i)4Mkngb8_)4OZfPI^jiRrwXvXMktVZ_mSfl8;{Ozku?$ zF|LANr1U@w=C7CFJ9!J;c(GoldY=pf3@G&^FE|a>7I-4vlKK-elWC*^QMK2I078LP zkeJsg$5iVLkv}E!ABaplk?Af`;+q2Jt?Yd7Qgus!_g4`+k-szUr?KxO9ogR*I0fY2 zL#n6WI1%OwQUL>(b=D)x$B0z!)l=CdB7wVx=4v>zg-7)_brN7IvE+VXOx#gfDRXQjcPtsSpQY z*$mSIoTbh}#L2}YiV?E`-pl}OL}0ZT#7%@b6_yBS5UukR(PQ*t{z6JQ*}6!tY1k(c@SbEoqmH4wxLk0(xZ1>ib;Wu^88W-Q!rvN`>hGsKBPfrK zJUi_?I~Ba>Pr>v@zqwYZhqnlCqyz0ai=LA024cysJ*-38t>DpoKbBk7WGw=ljKX|~ z5#Gj&xPB3bisRLu9k+gam+uI)@I5dIWi-a)se8)51Er-``*`CH-X4nw?n7#hWvonS z3pg%c9(<@Qv_2Y||5LlHoor+aiuyN(Y8)4Ga3F-73p_VIK-}BKjVpc*8gGhmuz;Qy zh@;MU;#~}l{pHYl?7~CP!!Uw)=VEe-bREE@6Puy7!clw zH4Ms`$gL#cQnj54I6TI|D>}8yhh-@}^oDv*ph7=Ug-*ibZmyxv#6!4uMV;|(d+>_? zudJ_xPwyMm{Zxl4410WMt2;;rk|SzS(q{<6lHN&ho(c|AyG$+i5DBOxymDX*9*m9bAn%R}YgB;o@Z{tz+ z-g!Nbx}8VG%$V7oGEe7GcMz@7MC(-0x|3+#HJBF7Fn158MN#);NBxS^x_2;n!nz+( zSsQqYdVoqCLmvX@PVXZqk;M2B)q0E)#^OG!;~;*Ybz%$5rWTn+xVp;lF0!H}$IRhB g{OW4^IW*Uer`nIe8SRietFVbL%|-1017JuZjx5X1k1?!QwP2RD0DJ0p7|3o~bC19Mv=7fShhoh6;=^4hV?ndb8G)%f>K900by%BVE9#)GBFnbp2g~ za&+TSiMy#%E3HSy1wYHpQTM`5ZJX56jrMmxJK8Z7`(AMa=4WAOWH=D`n(jBPz`g%!_}{ImaA*OOD@ z`!h=`ugmx7{Ai|ja$u^$d(;fUtk5bukW|!BC_KG*b_-$CMsIa+KE($F(Zde_?A=#Dcll!#*eDs zdd`(>CCmES8*Du&@rk(*d+5^aa~pl z?6YYdjDrz|DVgH&;R9_ByvAleWI5T`Lpady(WMVz=+vj|znh)KBTy?G4lCQKb++o1~1h&Drf1hXqunF8MDp{Z#mUx2|wE>uiIvsKI9 zYiq4w=fcaM)5fcYU!0OvYN^`Mf=iiiWE1V%R)VuBsyJ2c&flEFeZV$a70jKjmyyUC0-U18S7n}G{$h<+xN!h$IF3JFbJZr5g7 zP_`J$&|;OAS!P+VGrEtNwY`^4kuw2TkqJlQNI<6~fHJ=7g`gfpZ+A(6j2?3`M9+Y_ z_Ayq_*_8lF^gwiz45 z!x%(pJ(FIq;12$e{F3)yH{pdhul^o-c}C$8%!0z{qF;h1OaRngT_lCBU^_FN4Of=P z;t%4@9~{C(j+2=8Vm0tdi?S=^e#CzpSo7PIH${V&UKHIZ8vU%TnT#!%cgWsEpt&=m zO)DX#+-U^B_K2Fs<*{Z~xLsA~mvuOSm>{4NuQNaV8Z=~o!?Y+tYQ?a)46wOANl$xuEK(FA2 z=?lh1zLxItL`WcyLdE{!Rg`x+*k9S`S_S9UBdXUDtAIE5C^wGsW>y2d1EZn?$X_`q zly?=7p4y{9_PfzV54OKWII;CdFrbe;$FCqjGkHL%><;Pv9I`nk>|#iVGoSMEL~!Ye zrBhkAIM(8Ti8~Bw3TGiU6h0M?-jui3XKNEV?3u|>D4Ny>N|;B5xXa*S}-?) zG>F!Sjl&-cVB&9p*>Zd&cycg6&+A}y;5!nK)8UdN{c&Zu^c<3a4EF_(vXIGTa5VX? z4NaG?Mb@_JJ${@|mHiHxITcS+{p9Kt)$~o|DBScuGS2@-4-O0q0vqwyvUI~eNvyA5osPk^>)rV*8a8d z_^jqG@go}+M!J=MH=S=w;JSxrm4t}ucgnQqd{;^wQepO+i);lIASEw)i%qVZ9vW%& zszDA_Tb;B=nw|IeEb{k1o^<_GO@Zm(0s3aEMSK!m9dC4Ffp=q7?|O|rL+zYOvg{Ra z)@tK93Cv45{C#vaIJKLrX=N3xT7T^eN`7$~QY}y~wU4WY?HLPJhUGxfBSKKe{erzvPI_}V&VGO$T`!hVH>n@&75x_msr!VYQ;I?o-LzprAoeh zp=@EORn6)8^lYaxy#iN%_62e-c|t1MZL^>JtHj!(0|bK`E->OWp^7sHxk8-e<&N8= z+e=hxLxyKc8zj8u7pOcsXEkhjVns^7)J{{ylu~#P!3D-j+vODq#$#XaA1iSDs zBNvu)#OWiQ)I4k6iM}W1nMdr{c8Sf_!m5A%(0`t#4k|xC9q*VtG&x)`QAFLPQRnj7 zNXOE32xOEj&It*&aiWE@HK`tc;Yvj^#Fw$+$(o!#B3y&N3sD^IKg3wA6=Dx0STW|N-7w2zgxK4Q`_S#c^`F$$bR2->0hoGsf9vn zn%w|KGaY74K-U)igeI;ZqfxmD5Vk~EW*$-}`&{+v?g_#_Y|`An zXZ5OAkL}@o$@6Z`G0&X_WmR1%IF~2&o;*dMc4VbV31ffcQ~VrR1+`I#f$jT?g&)rC zvg$!JCni8ZCr?lWSE8yO?ZyGi{#v4&pEk=vRV71J4unK2{=5JLDY4l;euT{ls$HNfr3}iZ5h|b3b6>%wWw2DvpH&Yz@o%yIp67# z-ay&lWLS8-DoD^Jppw%R)_3^D5U7?QUK|Hxl(?6tRr0iI2Yle6EEECS_nj$51+Qt^Mva^BbF zOH=HDZ%o5i31T6#FQVp{OJp8ER!8;6x_9xAouS@z_=(-kCjIrV(KK65>qQDm&l@zL zpDj6+EqPj=Wqpae9d2jBVf#s~c>>25{l_p9m{>=;0R`nVinl2NFfSLD$45YIme8Z|V=pS+BU!E?Ue|Zld8@iS zSp4T^4y)IJq}O5OmW{Y~7^{~8Wha@*cj-?Zclb=q>m-wJ&7V59@R_LBY9`+k;$9b% z*IF^(mEl`IlsiAB@SBT2Hy3TUMEBzr<%GtA*naSFhMfF^Eit;>8dQc(fP=5Cl`sB; z@C~Ccdo})4$r_HsrDAs%+KJ~oIq4fR*G_4d4bU%(#YtvJt~=+m2TOaBGS{|&Djn3@^8S^RHm zUxKH8_#bMo!~1`zUCIS_1;rkOWze#Pc0AZ5WIQ91Yz{>zGsz^Im`XWCYK|Bbr+|u~ zp#bz4161hHbAEL9*uq|$kcsgF?Fqi;=6 zTAY3l)KjKUzwdelsT;`=AH*NrksKUuP2$zV5(d+dE`&fMK7N-XZu$I)t=MbE@>id# zi%(pKH7DmC`QF?eiD=XF8bOPJ@Q-?rSr@{;aJs#>w2S^>)3Yay*!jbubOt@&qa!I! z0fX!u&1QtYnm%?Y1|ho)?Nm}%Xj6iUkbEb@rpHcw0NNS3Xk(#yaXlOdENz764N3W_W=ijPIYG)$Vkl` z)>NxDVs#ye7TGm)74YoIOC6f8r%hHq`g88`Eoh^VjG&XR4*`$6C-xh?nm5NHB3|z7 z_PfKo(f=CUiil8UkIS+jch3+T(koj$d9vv$g}@Nc3RADn1Y%bQ3BtBWp7BP@({qD4 zguOW|>^rcGB1Vw0h0GT1`X8dJx4U6s6J&D;;(S-0*91$aVe}M8jQ;NOACjv%E>3t4 zJ<%YB;FQ_U)w=!8TO~~Fl&9QU5bgdy=cr}}7CHkMUsKu@s$dGPn}UH?MQRuL-UzuI zTes%rxJ`h5s-EBbib&Y!zz1CiX#;0)KXh@1(|v!;0TNk1Rc72JJp}?e7XTf~gx5kd zm@LS@WfI#9y*)SD{CVICUJx&sh67B`5w>OY*od|8=|cGS^WgEMB@axe3L4tR#ztve zQ06R3nORDfy#?^bt*aJOHp9LtVgL~ym5nIpSDfk!yL%|d+;4XeupWu&+Y7wN;^=gl z!`(TpGFjK?)5qF97$0w7#uCRI^N8E9Lv>wU;a*KruhkkD#=K82rJMq*xzxc=aS}7@ z_GOp7!VMNrr_skHqcGpwG)YM;hh3GI0@8X~oyR5}TpON>L#y5j<~#X$h>mc1OvZfV z+9@g~WlVDlS7rRndGW(>+8!&Dr?(-iCs|BcZ*U>mP%`)+q%?GpX2-9ugaxOgG$SUg zvCMYjwu~4Mt;*uF7IgOysXsv=lhOSm_=+5K`L^kqs~kK6P*bgz+d2=MX%VH+F5^~4 z?5(1Xb`;3pQ8pPc&z7Cn${ox;_}SrXvljJWun6q%T@e`7Lav$&{RX{=C;XI4Z5vpA zxNiy6ni+%S=8c)QN`h7sZjxAaU5IlBRW04$-QnURy@&`)C^5|x~;=wuDK5MB{eT4OKw_VWSO6$ zPaL|q$)KH3$H?;;npM0W6r0yYAVKPlz7;aLRHa8ym?H0$6gedduZZBirK7vI%3XnN z;v*|`!l%EX08;72cW%#8QCL~6jNP93uvqr^?I^CDC^)N_uMmd+STTWIp=6J{k@exi zc5cdbTiS~QtIV|7#LlweYLF}R2(77w1F9HkJ zLveJu4*#W@P{{sRa^Ey?NriO`-OEa&SpIYt>(kFu0hF%lsLV?5jTL+iEN&4#&Y5*y z6+p$R>`XIg0pnRT`wFQb^x%XXq5eGhW+$Yp1OT;yuKArn$FkJRAMsEK+(xFXEVEhLN5%6E;!nLq zO*D(NO&PtHOL`d2T8_xMYE>Uh ztKZW@+>yfWCCd8CNCO@O7r}^FshZ%KOCgt_`|cjoGM=c;a0hJDe24r7)cxjMCqs=6 zFI=rpJcu)85D}UhFUX@WYsTL$mp;sowo3=oI9KWv})}64Rzf4X-ad1+~oW~DU z@(ghPVTO|+>cJ(V({0a5_`yX?Frc3x+^Z*iddIlT9f&LVG4?@-@u>98i^ z@{>|h?xv@)4l`kAFcDX5_!OIX0I!stotQP}2CqxyA~&!sIA)2CK^3NXJ5xw$Sr10n z%Q7k~kD}n}%7>QU%yIYAq6ftN-qtjyh!39ZorLjYpym?aLOr0HV$_kH^&}Zf94TT_ z4P3?4YC8(WnBI=DfC-{XIG3E}fOZ-_pgB8;wvB78bX-c<)jUc*I`>D*OQg0UypS~) z1%6LdyOw2T9wm^v9k9~$k!R*)Q;l{)lE^-*>$?0TMxtsg51S^*D5N|>1jRKs!jn~b zEkAHx7?cLum>!@Yy7|kY7PQ?*xrfkNF-zxB4@acGeH>8I zd|W4$c-Bo?`s5ea`eNjvKXJ?Tq7`wb)C@Or2(NK`m^v6{&FT7eZz4ezAo%@^1#qsm zc_=7n9W%<|w&*uWO`Y!$if`(~AdrP@$9&}o36zt!P~S;aCBEfP#cM{OSPz+@n_@2! zb|U9f7W4P3w%*5uvi>ga8@#N0GJ4mJ$%!#f*%`)0j!Rc6){?J+A|r0JMjvzUrFQY% z3Y9q2=h~i}C(pW#kU*8`7^&I;IR1orA>&i2EI3lo&{TK$#0X2+f$+h?OFAF-5a(k$ z0T~`6Sg&NsPwJ*h*!k5lsajinu%|+JpFk+tPj*Rq8apDVXerbB$a$<1`3*FXKoe@lJ+#POaX=*TdP6f!4BnW#HdRme#=>?v@Ts3ijUq^#*+2d*9y~(OQ z_YnrWR+Umm0+bbJ&X-920)5Hj!UvAz3auRBP=nr$c_uqLA9=EV|M#r^% zHK^}Or!XHdBx_AeCzY`hEjZEEti{ZWc-`}jik0bpu8N!S6XR5vWqG>7UX6r|fT7M^ z533AmLe#O$uu?ImKD10;L|So|h#U*G1*r117FI~^3{UBdZ!RJG)$dMpJcD9A9J&5U(_j9!5&gMyzFopNL0w*Na?FV*id)>zaMc zL+Ej$Er;``_KCzOy~d?rqQa0VlOG2R18wh_-xeHnP4?!HxUYrhUv zdTtm0ZDAIv#Ga0%0e*f6zk3erP&pbc?ek)AqsQ6?*9QsL2cWJE6Ddx?)ab9b=?I&ATgR`h2;5n_;C2-xaB(i-zbE+yJ+NsJEYBSs47&|$0X}~ znN$L+IUDEds2_ya}h%OGzHfl9H6O%M*enA9<4N$zq3{2a|X;x#}|Pj=wq6YZ)}f zNiLv5hNP`(PvcD-#cyCO@#D;#c0@Mv8`w8pNVF?eJq&?L#A%Sb4KQ`|PH0>SCWKLC}SV~kT%FRaRxqpi(JX!2H zvfZS7J;HaxR%vdy27RyAl?*VAw_V`2ruAQxOaYRJ7wJl-%Oj%0z6IwT>|j)J=$`3H zQJKv7f|dvMh+CDEq>qb5VsFNkW&WM9ZS0ULi%!P=D&c01tim!Lr$krM7!c{v%dT7M zk}T!ZaJYQp{xb1yu%es4#xEI5oVrN6$dvtlU5zy5uPS#jiEC7HEr(u?*!P~p!atih zkq+}E53#0XcRX*CurpHQQg(GjT$@ajIg_2+$d{k>KMza3R_3-r--P}P zFH7vE>qS^T!XiGO&$@qP{ok*9<8Z3P;>Bl0nyuIYsT$rB!(MTab}f%7lUJ=oOB^6p zW12&WP!=mYZ_^}bcO}@Of*7A7Zw_B~nJiJql_?*0+F{BYU>q+uoIX2&Rxw|oKhLtw z&G6k&q_nP2fAq44=o!BNHTs>#RAPqi>}{@r*jJXvh=?_$ynto@+le7D>+TO(+?6=f zGsZm{q%I|`PYIrCSC*+p%hALMjKO-K^~%=UX6?7!UNBRTGH#e=Ekl0pBo-M-#v}tH z-`GX3FOyuQM||S@peC1u!A}NnUAQiGrl>mHTXN$OK26$vGIg4FVM(0a`B4!ODH-2M z=}n<;Sq_^oyHiJioC%t^Rea(UraV3^&h)TFtf{t#Oj377i(D~7>4~%Wu)zH6Z4um- zLw8vWt>yD%j?z8;X5Ypc{6r>cyAolvC|`sG{po$O1R2MAJZ{+)rxtUG7(0ji1pbmI z!jj*!g!`K!&@bFShC5hOU{exy1Dr!?N^Ntg4n2_p!On~^SH$Bl**&FDQE02xxfQcg zMNP>_%0Er1G`RW5aK&X(Vq~HYj)6x}dILslyZGzXljpZgCoILilnKkXjQGK$e-tgk`C3vM4ITk^sXHz+R1^%`J#{hnd z*9R`3$E(6Ip2DTV@x}B~`s9iz{Kc5597?NlI>u~sdCXAHm@~VN^9^ysmZxQG(rM#X z5gLCK#a)3p-_pOcWW;s@?v0~FghWvy16up~T!+E>++5|+6B+%pJJ>E~^KF_h0RFx3 z+yUou^Gw4RpLi|6_U{dFC=16#27(w^cp;N==V|e)w0(FC`_Yu&-j(cr%^nu5hmUW? zb!6fUei*{6QCId2)omV>0-w9L*2}PAVd0UA_g8ZY62@9Y((uzDQ=RLJvcola*E9Sj z(q4gW(h0lsUycqG!#~EY>A&x}42)gRg6rFc5Ph!Tcs{P`@X#eO|H>5CdlMxcDvum6 znyvn^3kV)1i!BU@HccCTLe=&k&}a@B3QA567f4n(%>;*3rS%4BOigO2z5se6cPR;V z%l?h#J(2Y5WzYl2Vn&fEr$x2KQw(5AK&z)9(x0V1fqTV>SPzfNr6_+cPb;2SuB_gn z#hCZKf`u{ema6+C=a3YxI!eh7B{GDM2Ib7G-8A26r~~ z!6U7{oRgtyi>xi*9m6t@1eB^vnpl>sCuPZ;6{#$<1sDEP8QcpD7k*w(0G^>;-9}3@tL<=*6%Jv;-Q(vATb^#p@q<^6q6%Fmrhj<|p?yY)KmB6QfL>|9BL0(2^1O z1lbF_(ONe@Kjg=rZ7t1k%@+4@72o6HKUl@(NvBowCdmwr$HDD_-3nvD0lTbfcS&*A z?H*bLN})Msufg3R)pRtR1;-LZ*6O3}nDfaVekBA>(h%bFG4gKg^L&NR$yk7naF8J^ zgdat0U*45>USOeNYis!Pz3PwB_M#T8p;w!gOuK)q9D z>u%&xEH@o=lr#3vA1z5N@wn*^YDfg%Kmq_z{b=@deykLe5_usZ z&Aa>xr38H~ewyx_^o@{82H)mwXK0wZ7)I>?-?+w`n4 z?FfXQ;TEG;aJH$@?QNtTNpCGS~<5<<~8oB`Pv9FQunsI}j`lMw8sI zBM<{j8&a3pFf~vHvikQm8yG#>^#o)yT)VJfM4&vV4P-T1y?%eH@D^c#%-=0AZwOoT zSsKISz$yq`x?LR571(z9esfSAh;HfrCop^DdbR#4NL#dey?#VcKd^U5co-~nMpDD_ zKs~VVuvI$4_`n|FL5NGVb$?J2VF0qDVms0=PTE~_Pzi`l;$3ynwqIBnj$MIFpa)>C zpiZE_K&67HATz+oAxa=hAORSTvV!7*>Voou`u!!d`XmK}%|(Gp$Y!7_U~zDqM7t${ zE3lrqfh%yYET9g^x8k7Xf}S@ZpbIb_#G7G`ID2w14&t70Kpm9ra7PXxAB=;9Cp65C z8QAtO;19iRFYaCsj0N&GbkUiAD3alq2n+!fO2)vA2w||r-3JU%Lhx4N?gI-bfx;ht z>qPp-2}GL>ubDBva)HSQzMlyBAPRnf0WndM3SxuZ;hRrDLtkNuKe>Q#Ff&BHpnFvX zeh4TA{Z6mMKn~uy~M9UrV<{Ls^q&-GUiG&qcAd=t*l>GA-2GD?{ zJM>m0lz`|H0T_?sD72%1{QVmg^Y#l5sDR=he)|IR6Brot<{x((57Q_3N(QV${{FR> zkMa$xo(al0AkS+eF2`pR}B+6|d= z{&73n>lg6~uWyqt9=Mh>h&-8x(b+!i14w)Sk#P<(NG_a9P?R0R%YObARSt^Qs$Py0 zVr2vqWP9-GYAI8=engkiBU?xl8Z8W@~yb)3NKQ+0w` zdT}&2*bS>%!Mjwq3cXgdYW!jfr*TcsENlZ2nAr#2nOma~SlmfA5wn=c75BK~s94o#^7gj9^$MycAy@7cBD#_!#=;u6_9hLt2_)A5|N z$gwsCk``?k%^QCWEBgmzH56ZBp-k`64yBgrJ)D?krZ12O>4j@|f3o%0Qi5Dif?d#p z>gYhV+QY8CAN1Gqg3@}Uu7u4Yt^O*c~9QJqKBOoXWKVE9S@Cl`tx=jx9bsL$MJ&j1DT!RxoX<30<5XOmpRlY7yEc~eI;5T;38YaU>d#qmE)ru}6xYbVRBBb4%S=q-B z#t;|t@EcH0Hzd;zQ0(oBKDv1jhj@y3Lm3Se#RZhJ%_!<7a`YdGkshi z4#!G|w#kdiC;tgwwHAKE%oUA`kTfrmmT&8c?lNtH#>twcclillx0c=s<~Fqp4IT4l z?Y?1V4%Lf)X&pV@1=r#3|YRb|aFxm{oOFO+toJKVx2 zh~UbCFHAG(B9=7816Tl%t(jzY!Wb4CMsvzeT+v zqoYX=`C>&~WUHYU8t0W#zn{2WRcAQr95uU=a|A?|plyC=Q;aRd9oN#9I zA?CrT35&TUEr@3S6rEexP)3C?HzAA7Sy)rrLm$nBQQEU+Q%8*my+P1giQb*&|1Avc z=nR=IEOu(iQBN za?eq8>0{Ppcw{4PFL$9-kvf*pG)gME@Kih^*tv*T;j7qju>{sjWZGBviID29wfgL; z8n-Qx;A-}$+W&CzBD9WKeC}D`@!V$T+j2gyonHsQu7fVEE?S}~45;kUXR&`Nz1=L2(XX+hmMs2r`8ZBx`v^c@@ZH;S# z&wVb5v%l-9KBISx!Iw%*T!Q|vc8b)nn|M?6B0isg0NbSavv6My(}ss@EP&Z|>Ly~p z&nxk%|Kd~oKFv$>63$gTK2d z_Xw@6UljNmF|F%=!J#6A$eXV%g315m3+)*F@y!QDn2^r(6(~%=d*N@SZ=rt4{L!9& zG&u6a{*R$NQZ0X6MMAmR`RvL;Qes#a^%mMSgxVdiw0K;NSR`vFemCXnIYlA)qj&Ex2f0V42Fb67@YkR$gR9-pARp>$ z!Z8SHhT}3X#N7xP+xbi1Yv2okqg4Fmedi>gU!i5^?Ps^IIMUi~#)SB9W1S)KAIvHN zXs-=_P=^ZrXSMEf@3ocNvU4+ok*>;gKX8XC{b#w!2J~mT?gr}14NnVrm(ifAJnbs^-a_@^t>YcS@3PhSL=jEr{U_!LM9R$znOFg&JzQ+ut6^ygiRuk8 zgD-V0{C>0HBkBWz0Wp8LLv6y)Jp8l1(*$VIs}wZuCmrEuwG*I}$n2(1tSKVRcDYzd~d3DiqZtxJ<>&iJ$q? z|EsnYqJHpn9~`i|iJbB|%lC;?2|TMLuvV=lH)Cd+T0a|K6$HR)RB=i!b;IpTSAR<5 zS~8eS4cCHMpyP<2L5a)E&a1@o!ry)Dn%tIjtMq{Egx;*TRWY9E*I9g8Fyi`v>jkAl z^la}!qkC=KbL#9j)QN4w(W^U!B~7(w&rh~XW1H!kiaqvGPSUz9e7m9ogKJ6-w+?iz zxTzv0JY{=K&ZHBZQttVgXft|pCgpjL{l8EgajvaV;THb3;E2oqe0ZDWe7)XOsqjM~;x|1E} z6JOvd&8w2ssmLHHpNa2ddb#JR{3-uTlG4W5Ksqp(F5Ngc{+ zZ;%F)E3pJ1`VUKqji!!7J28fdZRyI+b<0BSXB zQNG>(mBNdf+c~bIKMv&X`s;BxOUZ)~#m32~GF0k|YCRPLK;96}6FnW(cLX?1z0&epI7~TvFY$_U zg?sl3&)6Tt3ta{zyjDrIugjD#4(4>{C9ch6qN#+&?5zF}I|o)OA9Cp&a&6)+-oVNl zWyu%%^Prsv|BW1T7?F?C&V45k44;CW&IGe;LQ_036&y+-yw0l{OdV*np)TwNcI_1X zRlkU@;(6Qs7MW*{KQ5W2!Du@Ufco8i#Ept?--{7;I*U|Xzo$<{q(^w15Wjwa$*hX8 zsUXre=CM1y`U|FIJxX(b@{~j17pe|*n5%RY(R|$f_n#h5q@3(z1~?byC=Et;4%iT3 z>Nv)6WZppN3lyIlZLVYT8aAra{YzDv2CsrYo6i)C@=m~Zi2e&T2_IuVc%3Trp$h?V z&`)0lK4YDGjbIttCboOeCy6MTGGMNxZF4Mdudlg63vuwnu>Y?iaNFT=+UBHX+nTRV zr!j11G#sATNXP>ZNAW&Td6!|oO*g7QUonEd8qxF>vM7&S4(?O808Rki*pd5b!Z4o1hRcA@u)UwYRf*c z+hmkYMGnSGS)8U*0JZ&4wsNr(lGS{ak1{q5zT^>Ib6K`zaYt61M0rj=f_9+eK5EZTX-eeKIDfbjEU$lrm9&8XV|Nvqdi|CkT3 zwzW=)V*V{#!criV3+C6$ex|QH+(%Q@v3a|`QZ6i{Wj0T)oZjpiM zrH5d2fjjbj85SeoRXA2XOo-?Ei8P#+efaZ-Ag1SudeFXyf&bbZvGIVK@WsrJ+>XBm zeG510&Cb55SlX5mNTRIbt(|30DiRS;?SmOJv)qqh;AT4Pd98+K;J<*rENY^kHa-1^ znGcDX8|tx?u*VA+-F}$E=3l9tA0i3WGJGT|hb(Ma^}ByLPSsO8A#Mv@&~=?MO|` z2dsudxrLqoUT5icci@rJ0HfWCb9&0G0y4dIPDF71^M~&z9cB&Rp{xw9MRZ9Y*P0d~ zkM|FqNJsX{^dUGuQsQR`Ve!GxczC(Xi}sZvFz?Nm`SBZM*5G*I99CO7O+Wi^y7UGF z`GZ#QG56*xY{ZwkZj^0Ae^Mjl)aZ+PrjHa12wfqSgDFLu*6w+|w zL-U|X+uMwQvkq>)6njH{fP6vYU4l;QPOL$5siZX_IUOHYizul%p@+Pkx!9D=Y*cQ> zXgF7lx;$=dUAhACtc#(+Jaju;-+j&ETse3&R@OW8+vYz_4yUh_=sA5Ai0vQIlo3B- zi&O?;dr`igO#X0fu1y;g9MiiNz1zR>v&2}bbUC+r(z{m3=D5duH@MsiP2=DnOOf3- z9826cnA?$z@!`28`H+qAMN3|jCHZ`_j=4qYVB{aAgj0GZ?H@Ad3-!34b8vW;JGq2Q zR0{O3Ppys2$QnH&OvpKIW_e|NCYeDyz}=|MYn*c{^8n7x8>^NE2qO7Hw`)}Tdcu(t zCbsnnVrf!7$K2!8^I8mGZri8QjwS-R_^W0dX`Z=%cV;4g%&fM3c2qEuhyEsC3_)_L zebKo``Nv#dB0=%!hPs8=#tiy~Hk#%eq*{9Tl}IrP?GYZ>$U3~-C5S=9JJlk^FfrAK z>LER`Kqc^dn=mynYl$bVT9>fLE4#RGLkcvh;Q|mFi#TNrScI;b)1zb)kKfbN-H9yp zo@MqJ#y{4rv>CX07jc}jJ7E5ZbgqrU*WThMZF;veMSUD%J4!1o65%&48wPXk83=pG zvf6&N^|`P1S09fD*NcY2uRwHN=vSWbcZ!1GHqMUwHd<5QSCCumr! zwaW8OkVY!_eN}BrB7xR7aQxP%(zq!0qi`RpT?6Z+H>k$h~58gon zBm+6>mJRP`VsQ`;UktoSz?UG3CMUDr1igKJkxz$N`Ca?75p$Z@g}{gfw^TBw z9$ZHzw+4}mX9~lbu#F4aVS8|{Y#!^a4^wJ+#UB+3zq$}OW&IMnb_hQP}6U#G`6;;b;goWW1(E9Fl(lh(Ya_k#-H*&ki ze$c1om2f@y&lbEZxZ;_9U+A|u&kCG~i1U#2S7el^ zA*ZS$%rK2H1&bW9%kCs8@(ZK5V7J5K6Kvjp-KGogor_8|MNJJ2J&z?fkJM$on~;|e z)hAhUqSx%thT4&u8p{9^%N(8@&V_|I znu^S^Bj|%jWUJ@Mx~T+hWwKx~+j1svbID*Dj_m}D-Uc?3OX5K5y&@<2FR|pv`|(C; z7}!>}j0Vp^$!VtLS_F?~Y|>iauSrp#YP%`!Vj9*3c-+Wmk#T4&DuqT8Y{V%mU7O~X zh(|%rW_<_1=yWC|^c@uRIoF82nM89<(~{)yz8Mte0F8w({7hDE5JdHrErJK>y2zbt z5RI|9b1sU+Si`hsf<~qVz_7&D?hYA)shp*g97hN92yn(Jjf6Y5lskMrn*%B1rA8uco9$XD!|kk)q;Y@zjeK0-8&5PzuX< z9`b$c^HTvJo=SeX-tq~S5K2OmciXvrq?~?Ui^tUEgXV7h*A$H7k%A(IaiCU(qoFFJ z5ZnC)BPCqa?2x5Fos3=~oph}X`y$8J@VTnl&VnkP!ZEUM^EmFT8X(8q+R7^wCVDA6 zo}5U;RP}mVr%eze787?m0DVK!F)7KJgqNteiGoBXJT_M)e34w}xd_7$7n~)o^Oq_` zvQn=hZugHLGp5?1IMFfT3_06>3&f9=@I=k8+Z^GbgTXJJ{vo6(cFvKKlv5&9Y}DOa`ud!B^BwO1$~l zM7)NF7|R6QTkUi$)>vzz?bL4S8fYP2T#mGMJBiwiycT6fAKU9Veu%qw4cq^vYLuFY zRXnq}vELwSTy`qLht8(`udo0oekdu=aS|qcKf2g_vT6AbZXz-_-QMvw2@k521mog* zhxa5uUW7hFWo?G-3^@sw4MJ+m5b=>>(tTxA)cXDMbev-&3AeMN1^b_AS{wkE!DI6& z>A4ZSE2aOgAvu2*RA&nfDHa91JOQa`ktCd3Vy=~@VCZ}-X^dER0>yN>I~_9Lo;DuG za;rx(86c80eB@t1Hc#-_GvHbe{akY^&lU1uxbq0hEFA@Y+U5oZT8}2Ka$bWK+I8yM zt}J@*5a{xk3Tv)dcuqe3ucC#7kouuqJ3;lm z;6m6u2ac6E4_rNQiHu=SAb+(txt+EN<0^_?&CSF-6rVNQ`ZDSG_&A_SQpox5kb8A& zC5yCEoZ8=gZ3_Qb&-o7Pnz>Avqgo(5FL&B0;nj*~{~zx^QDgZe^Z$&{bJ}28VyMS> zXdIT#k-e!=qdn?Kxi)3PJx>Br&^Ylgj*0X|O)Qw*S;@;ygn03zSu%`BtU__atz`aS z;karrw-RGZR6yUPyLbntVfS$6@OqlxogWUoCt;L&v!($~nUFY;bhH1P&qbe{YkvI; zIni@Uk|(}E4gpDS7Ddq_S`x)dI8w%1HqB15Zbn*W9?#cScl0qxDYY7ADB*}tM@G~d z*TREyfG4iWR)}m}bnuv9f=5-^lzTzRK@=3H$a#899_|~4P2jcxYpN8li)`tP?f$36 zV(VIx!ki|YY>;d+I?TjrrBjo4H70X__q?JZ?r#+z_OIk>-WZDXF@)7YzjIxF#(|uo zmDIugw{Bh>)-)^*&WWgE&y3$0fIsp`*PtOCqm2pgCCM%S{#Rx10HaCtuKTuaOxw1t zX=B>9ZQHhO+qQe!cF)&#e{D|t%>SO`?tPPebML8DNoA#y=gq3Bq>`%V{XHg4juVmH z!%eG_#>B&20M$*E?!D1uHQ#B21HEbT=mF3rQwCsa1-h}n)pq8INn3W8`HBwQSCz)B&$3V zI^(TmU_0^l+D6zyFU@I{%7?e*r#5z;qGAClgzTb;lb=lc5sevn$_bl++pzbrKH+0q zk!LBHFubrxLO3xQ&a#du?wKXp;qJiGY`fR)-Oph|bTG?gdz51gre_upN-o^KN8%79v zfs1dgN`m2oStxq%_=KX;>6jHo=4QCx1+Kv6PSOHdQ+<45ywdugEt4iODXv8uDY)~q zvN@W4zp!mKmB%cXN1C`HMzXCRaPo^O5_AbonBqcc+M?Thi9w~YC4LB6 zwnmjp5^xgUs+;}Q-ItNWL%wg#WRx6u3n)^B{`$=#Sft!|rJmE4-kWeUPhQOM5VGxQ z>QZx@X;y7d2(l&MFLfAgAnMv1N4cCO=y{Xk@PUmZ{$Ymr{^~5t#$?`$w;!(bZ|~C zzG@#-DmDxJz&EGZkX(`?95F*+9}7vcg`X|qJaM`Srf#Q%+5Dp|7~tYn?sX-B@i4Qq z;!%q{CbzPo=KFB$#l|6OT7@w8Y8N+8+ zei>mb;Ygrti4ri`0V=$%?Ss5mnlCb-JU{lg-6;7?_`>1Qu!|r^ov2B#y#|iYhnoo` zHr|oGowJD0jQkG)<;NZV%JErZlewU}zXrD{CC%Q+a*A)5U6Ut%8l|1W0<0+0U_V!7 z1zowqJYCr>KKVaNP=~SWJt_feP9((Hg-?Y+5@M~$c(P>h2|$_w$o4X zamh+t{YOU>0tx^6Lr8*}k|s|}Vg$G8Ic75{Pwy(hJo#R7MPw0~syWF+?BqSD2e9j$ z$hhaNIa5o>I{3Re7&9Hm*t{ZUhQNFF--i+&@@K8F13wWUCQpE|3BKMI>@9S*3wcWY zZ(WNPBm)kQ^L4sz5-sAT<-nAk5rz<>xSc(JL^GRMSgUu%D zwy<@d%P-|NP1n1Ri()w2cjj~%$;K^)H=KUbdUUc!au3}O}G})ZH2jjeb z;$3DFOq@&$&9NgWKU4nJ?Dnxcvqdk(9cK9~am-h`^g3PMfYn|zNdmv4;Bv?j*RTFA zxFsFD9h;3=iY8gvosNg#rpMpWY;RCuonF&e7N}0@G}X6aL(MbnN?uqP3G!@1zP7i< z!1&_2yJ+{^v@q*sn!xtKbC-C*e+_{ksDKR^LR?sAJ2y0IQVi{qB_(N4jl#`H$)ZI> z%|jELN$#vXrMp_<*hBtja|{b6Lt%l9NiG88E<_J8-qH3NTWg7#FQ4rWhtt^^d)BJi zpV#aygwqHM{?EJT<~<=zW7D3eCtIl=HNTN##WAgokMGIa)Gxn{*tYA=^?J~0kqOXI zOFwti`}GM3Lgdbo4Eq+f$NKwGPI8b0R!Cs&hx`<+Fa7R1gLUjp*S4b+@hxuB?i_!TvLcW0uv6_@<1{)@%oMx-izfU6_C-LEMt~VDxKZvAs)+baV&7 zBB4Fevp>cUrHu_&<^UGT4!Ti@K~f5#aV}<0k{@@am-Q|-u$!;le8MS#=t#_)FyDA= zao1RsGk4fb0D5qZ3CIJ|?&K*L;bu8hXi2NY4x{7Y>eHx$HH#P>=T-h&^(81BpVN(q zM%1TJnCSN~^3-u~`}i7X_z13?ha&VYA|luI=pI^vykmYWA>=O-Tjb`0q9DQTs|ahO zV)nD0AZgmA@@U_crN>%=#_H~kmi^kSz)f6pHPkG<12070By6%GD^mV{R>qP<-dye- zF%IumRJUX~_TrJab}6M2Pq9gurf{WMpi{-=A~^k^@nPcp;ue_|Zn27-NL5#I~4$uIlG=m&-aCZfeE}whqcG)l=QtZPnG@>Dp>~Y6%6KEvwa9PaM{2 za&BqbYC0jI-P(W6*Gby?tHFd?Y*sF!tF=E?)N5ALFk9|aVhq)eZQI(LtL5uP$aLH6 z)vxH=>g(G@>Kdx@)Fz-qDLuSJOV-v`XWdoRXR_6RKc8r=U7DZSBCDz!25)NLXeHGs zcF1|EtF0~8F%h&f3DjC>ZEEiLY&iXGEYyhSFSDn#CEdm@tv}S~>g!$pH4?0=?|Zn* z#g%xgZ(nM~TdTTo`q!9~^}Daxj;^b%t^ByHN^oi;dOAtM@{uw!fa@wY6%r*Bpe8u^ z$?u(?Nm#_$+lRv|dsf~pgE^)V=iJD0Z|T+Q<2rYyR5H$2-W~nB`OfyE^=|C&*KY+P z1Kw`;$K1u)k{b&d#uUtqKPf3W83uc1Q?JdGHQ5IX7!~D~*%yoWUD?SMG?3W`t1BXM zcUDs{vpmWw5;jxVayT_shwNs-<+3ZO;Oxgg2?jg!)*w~MWDvdS>mEW_Ngl^bXfv)>Knc*GtEs%avuruMU06;w`aroiN2EvH1}v`T4WILxBT z@r!6At*4Z-n^jfPt)>>Uony=CJ-ArxMw_yobIb7?Xs9gjqq2RIDLX7>6jemBjjgAw z<*e3o39=tl<+Ms@jO3y$WmHv+vzygbpGl0x7VYqHRb^ND4@(VZM$&qW&F6 zPte6tf%WA9;~NYRb@63+%5#js-)0NFC5~!S7(# zzdFB%KasELQXdFhgu=T8#Ld2D06tOq6gE>(n|(@9^OV#ln~pjA3*uNL=gL}mPCRF; zj_X5u$R1?L9fNwX9+-ghY-jil`?PH)VQ(P-V`mMCQkR1JuH>Wtrr*E}09hpp`R8be zv zFfBZn{ypB8UlVgzVGx&@KwCLF|xwL-uaxdsja}%1$PhqjPUM}#g zlAEQu?W3%jl}nO(C_wEX=<`mKMfArax#rvVyZuNzkpGL)k8FB@Z@M}CMW&)+c1-rz zveFvSEt@_ykhqy~A7Ccmk9K4u+>d$WBkYfIBq{8Vc4Q&^47XI`NMixFL^q)oY>Q%A zLYxD^qmwuroJ%&*6`V^o@fxg4JP{WBugQ-hSeJId$&L`ze)iehZd{J5{%~;G%{T# z9mnYr^JYw>14@zQtuJ)KMX;nX(u?l75_LYCMg37y<}+Ci9gnk177*GqTY&IHP+sab z7JWarQw#tV&(U&-{b4Tjg)xumo)sRfTB zEsnQJ$+>Y+rsT-f#GFG07tbdhK;5;`8KGHnWPUgReb)$!hmtiy`Gr%Y%X#jU$nQS* zix~29Fiz@$wXax-)-=U}U50D}BcOymS+345Ridf5Tf&MxhqFZqcO-fm_l`qW+N*>; zUarA5^>@oswS*N|j?y-@oH6t&y9_k5P`?v5$|nyIE^mu$2DVFAn(CN{*!0X{)@_(^ z-n4e+Bv`Ymb>6sk>OtVJOdULqrfI|hP3J0TepyxY366u>r4gf*Rphxqe5A-Ij#8bd zGL6c^=}TKr?TGTY%$2>3ef3z&C|TR7W(+myx^}Ylq)swb+o|ZB@gy&=*$>y*vwI%$ zDt0sP9$yoRN#63Z1$r^+J<;=N5|_{bWrKGZ8;V}CS*4S+XO=Z58kg9BXrA`SxX}p0 zwY#c=XQwr38kgJ#sG)Qe8z)95nO=3O2<#pDc&6m&(sbAp2p7`tJPhui3~lOd_)=9t z;7ZCmQwCYAPl5q%MA4V%(iv+){ygYB8kPMagS)*X8kZl)U)j(JGFcq_fAI))vsrDtZaerp>CM*e_Hg(k z@A76t1ZMcul9e$(F{z~3+-EDfVLrVmZL%&6P?JaK9UDjAeUg7Ydo%mvX-*9!d(4=J z%X?tN(I=;Kn=Wwx@kc|`(nV+fjnn2q{4NX`FVqACe5kR$R0h+A>4a*9swcz?-6**v zTnaB*!5by~rTyfKRE)9xX8;x1`Z2wlQnSt5^|{>zVY^Isu9lAG)XdeXs0T&wp)YO9 zbEC?Jnk_Qhk@swYFP1W-Qm&1%zwhV|6n7 zl~q@ZONSeQ0)io5rfBAuH*`gDH~ zw-APg=K8zX&QEQxiP~CZ%p;N@x8(7GjTaY6N{5aVbqd6x+(BSDbw5I|77XPZuBA-) z0)8|@tf|zRCOy%UA(DnTQvx4en!g17B=tS}6MOHXim6pQ3y z`yj;65`I#;Q!udr&6J5W(Z9{jT@mo;N18N*k!LD3=h<{g<_4$Hk=xn>IZK&IMb^6zw6-6VW& zSjd49ZS9$K20^T7?~FWvGGNEJFJdn5NVyhyAHmu z&AkCM8mC0=^9Fr}pxN5MxzMQ3B0F`;6A^0a>>9+$tHrsZ4%n~h({n{;mXaEjt9=uT zaOzN4b!~DAG}MSTXj_`);{m0|a;xW82V+0#&1O%9k{G2Z=IS33W5Y=GiU!0gn&;X3 zBQhe!YRbqBx9AnN^_+jCDa;G;2FT4v6@Qd$zukO|`H%IAi$+}A;FjyKjffHaGPz^W znLz52@7Jn#aCDctRJDpDFCY!0pdb~e;2<3%mlV^cYE{c$h*m#`4z9yIs7{A-S`|tp}`9k?44BpCCd};J2^HJbrsxKC`D`E1VlN4G? z#G!aTpw$lvNcHEm)xX^@yKVistCmo3Cl9s5t^Ddiv+p*E_DIvhY1b-A*9QERQ{uih z?l#p`{Ju_ZSj8}pfnVQ-ji14ZjZjKTFG&Q54-wNtDhSjaVjkvI@RZQT8bmi|cY!xD zw*+Kn9SQ63Wp46NH^Gy^s5LPrV`*oQJ_P17yX4hTd95Am)^_c9bkJ*QME`vGk$*>P z+WUj0x6=OMQEb#qj|=4s^J()5eBU5NWGB$^SLo?qIR^<1A8^$4K;>sd$8Yy7p*B2p z)Y)!SC(9`uL81$9o$R8BEDO>Y^KIct?Ic$w8XC>YecbsWEh)KivO{;0vIyE591B*U zyjUTXq^5@ooE(@rsU$Vgu7b{ssS{sZqMJkZtG`TP)^L^%&jYyDO!cC*K&H8WCK~fH zqzj*gKY7e%(CfWr_UlXPo($VdGpZ_Zyrol5f~n;z(L6w+GQk9W&n_+t6n*C8ni*wBx(FkCg&itA;EgxuHO3_8SDXLr` zieO)Cmmur*g(iJkHK;9l5wx@sf+$>?0StOu_FqXwbeUhvMs(`;M2>%BPfO#L{3b_WO9%~_Z%g3Pa*xc{zgKX0TbbS%RubIQ$e z=(D{k7v!8-EgS1^Zw-vZ_=p)6E_;gY0EG#mgjb}X#20B8xjNkkUx^mz6u2WwRxu)k zQaxtw*+YV;ULw*yDbl%tKZ3}+p8mt4s%ry#@O1qW%-<2cs(2QRf)E{oDJow=(edG% zo@v0KwbO6(7w-4VvKJTAVtR7mbSuaXm&MjQq7DU1#?8??GT+P$ywU1k@-L?25`I-d zxr1+#V1)w*YO@LQFV2`8$~zCM6CxU;e>0kxEXuois}nY>vFYqlm4yTS=oX-G%3u0W zFAh$ac_|wF*A-O<`@If0#PF4nMKy-L&dKF%k6cI(jkD0DM7zBDnpihtQ?g4}++a1dT>q!qt zTwj<-SV-EQbAM~u6*gM~aT)i&R)o9SAO-}BJiF+Z%GNX^96FaC6eL)|r6)cN?7R+E zi3o?2YcP0T`&2hB{$~la9_UO`AZXINj8Ypk&0|2_)(F4N9&Pm&nU-1#o0^X*jDp4$ zOZ8U4GUnN#+B>+W=W2eSh2v^IW>>=x(ePEEhwUhN>n#uJ{3$G3gZb~9%%@%y6~jJq zw1!F3Zgo>{FU4}3OecmGeblVpwE}7^`$_^o(J5eaC)el1wyuYZ-J7FFORCu zML%B#&yWmN+dbdc@`G=>hXD(nz%CIKk-eL@&%#ZY247v{MJeFpxy4D-_kL4S8js;+X@2yxUYjPCA{u-FC8PKd<1%zqc zB+a)dUqA6Of85Y{GYxOthBs{^KFGWZnXW;Pa_oG2n19CkqAz_A1q(U$qD$gNn^P8S z3j)y5Qv6gTRO(NmhmNTa3WAr{8s%m>^R)^{?IQ_4L?7PvsO8x@7G%I_bOT0Z?nyXT(la=a&i-h1Mw`3EmQ&V~#a^w|9U)zkQ|gn&egXlTa6Qf`{laFYiyr^B^q2Bn zm{QI%nV%Nv?w1qPX2@xT&jy#GFg|6!*^Dy%uc7_@y3}({uI5ojCfo8qZz4l3q4=Zw zn}Ix>wYRk89nMvqC{)-cb+=Pc6e~R_`FCpBc{-*F;&Wc7 z5bgydjDRZbGTJbv%Wa^tlWret&v`CR-YVHLI70rv2S(Xiuf+RTIAK78n$gX9BFyW= z5}iwm0@WUnrUc+z?N$BiGgCdTwTFy=%xtfS)g11zxqBBo%;L!G52ga9@LVvOLy=gK zd1RS8dYL%j4mst#i~RG`%7#D(R^pVu~=KzEk08F17UXH1kEJW-enn7V9=1WuE=@Rll9^ zX!uuUZiMa4@r(~h_2U^+`jZcPK>4EnE-@x`G*t&)-JfiwOn9?)%)auM+R_Jpjku>J z_0?|Fq)huW1^$O(F(BnmSL?*aJ3WqMC?k>GmHj^h7F4z${0nJV!Z4eyegCK96SAV# zt5vVGRq-hr9YsSh4Hto%7n<8XGHU+l!}9T^^s2Ya*dc>g#4CM%@}g*oh4deIwON(F zRG2vVr)dr@#Os*NZ~Sm#ZP0{LiP{px)_srs{v8vs6KTz|N3>dA`fd6@(=Xe3YO$); z;u!>MjZ?6+0***7Gkbi)EVpaUiD|Ix3JuUvfqnxb7kH!#a$qy3S$F&`%9Y$gs>ncGsp2Ln3S)vX{|^0;_EFW*CJ8;FJ8XIZ9eFusUWP0jlQb#N)-Fo-iTq{ zYczUxxXwxID|u*erlp*^Po?!Q{ighmB-?^oQ4Z_PGO>Crj#i|%CJ z+HMQ3mER~h{Vf{Dw@I2SL4?u72gAuEm%e6 zb8YVkx4e>LHS_+dH}HVsH@%y{^myu9Pq=zV(kJ+$GDgdDyXI#|xC)+d3ry1-rwMEc zi=r+h)`dEG?}^Q7arUJHroxUtrV>Z)T9nQawJ4j6HDcGP1Y+08c?Okh0czjdtnJ~C zf-wd1s=xqhUY$RBYZ@!)^{wpzaVYUD_CP#BJN@9(#hN0$U6M$K)<18A)x`N%kFPhg z>ZL@3i_TNYD&*ElPh@5l%PG^k7MW`b=Y?bshJjDXUmv;hj#;NHhi>Lq1d*MwI1Yx0 zWho_zt`uy_p5tsNO*@{MT7BR~GoAkCc^oKBAvkcZM%Q61UzWjahG`Gp0m-aC4Yyo~| zdKZlq5itr}9hvqSJj?=w%@q-Ux}z<`ahmTqTk28E>UKp=Wr5lx&m86l-%Pgk^PB*` zEdC%5sre4xORuM|KP7(!I-&majHFL5}_wGSb8WMmUVgmj3)uWukpFm#BW|l(E3z>f77` zCvr}oXdU1eXn8tq+#Vax5FP`95+-)OclH_D=!TY;P0bp z>F=MR%+P#Lq@g?;JSZeHLpk860Q_DKlpiOC>;E~`15G-C|KUaamxk?YthNK1^lLWu zn;FOijyi1EJ5oWjHCQ*=!(Zx5Cyb}3rV0DMcgzUz{NHt<(PqgJooR}*D99f%X^t`u zb;M)sXaR1fsqGto5vW#O_rFfBI6nNwMUFcWFZpe)b`4J_unzO)W9|uGZ4}jJXz8^! z%#7@5b7njfjXYL zt~#%}*I()0R|t)p@4&{*Kz)$wn1zgzXPct|GC-#UTyfdIJbWQ z0=|jpPk6#?jqei-?A z?B6El*#_kI`oq34iLc72&2hiYNK-j+Gx*q;SlG&~;H7=k2z$R(&J3;e@{(d31Zr)( zq%;eFx%$1z$KjlIerJFVO!^N9lkLcP`M*Wt5p#f4q+H(imF~?hMXwaULBXNaF0pv+fShN^I?B zozaGIHr`$*bkIt?K0_~^(7!EGkQFLlCz)@rOq@B2;;f7}k(mlO8oxcfDnkW--z{l4 z-^8dctr|lJp$O|-LxsQikUu$CG1iam>;^udlfbNgR7mO08>k1+f^CEHqtKVl_tgeG zU|Ta0aL$(&oeK8nPkq$BdOd?&J9*#n3#m1wIE1P-p|)Fo>@*7dvKP_MEo-ww1qPyykBb;SO=T*Szu;Uw$0w zdO~zLBSicF&H6rM^~p~CFrPKEt8_w$m_^0vE3Orp%w@x{+XB+>^}8qtjk2nm>P5jc z+M6T$vXuQh@mQZ9K&Y6VU|NM0@jJn6twzhjO1I`uz@U@VuPhhjxU9#uFTkI+a+ZN-=u@x>9+4kHne$%07kAwLG0XVvU3-nY?n1Jekpgk zmS2OEX-Gd#OCkd%b??JS;r(#ES#T-7Lr;Z0=|9=J{h6+V@CR=<7c|-Ll-U=%Wo^!r z21Q+UyYZMfQv89dI>;k{f~w#2!p^jIIe~bh6=RhcGun85Udf11^1DIE2Vc@mQtC zLkX-mpf{qDK(n7|S(1l+6dyEKDu(>D;I|*uqMfhccH5q25fC>@Q*fz5W@=D0Y()nDY|KT-ZJqTttFJ+%iRx z+WGbkE(1UTVGCMN|2^~z*V82cbzyckBI-H|JcYdAfeUnxT9{2ro*X{V$^vCLNPstA z6Qh!jpK@S!uOnxfrqiESVf5(W*$YO~B`$9$J`;ICpITJ6@T58soA0ABEVyJA$zKU~ zkn%au2_xK3_zwtm3Ka;3tss(Yngl!D5t*>v(res3Jq#Nk)@;d~=L8p%3xN)nicK?F zEDJnP)Y{Tug;I+GrjGh07 zfUH9_xUm(-?N{O6+XrZ-d^e8}N;Yq4o*CF7HNn9C&V|Y z4Q?88Ag!0>+UB}e#F?AY7%>EUq5VYnxM($D==~unkChIm@1lQV0>lzb1i=gzoD5_V zLOcQ($}bkL`z694ToHtKuH|DZ^PW3 z&o4CAUqAYlhEQ_x@s^_YG62|Dh9G;0lLnQnj7}D0%Lt%WQ}s~XkmhpFYW{c^#xRjm zjJ7khfX9=UeyR--T-?#sFM48-?K1dE1iw=I67n-JozQmXw=w$xDFPXwuhvka;f#ID(Y$cY)d49 z2~X#yhN0X-fwJ7G$^Ml{xy6b!<06w(nY-7qLGF$|yby4|5rH(EF!(%X-|(_L4FJbR zPUx!uQm}#m4{MjQyH!(WMg_!wo%@pqhS5kG%VOc0}l z87!nB?6!Wcqwpp}$u7dMgTf#V5VL@14;o=V8eqT&hWqa1&VYOjU*hsBt2{Gt{}AZ) z@~6ke#fn+_Z37uGvjl62ni%qrRdvPNjR^-1LIv`Ms;fxN?KOjfo(ly<`&H%)HELlL z{VB4GfUKNi12Wwm7qY&AA~K+8$-I`i&xn2N-1mgkD!dV(UEREnDbgz3*}@Rj=FOrg zG(s>G^huhrScnYG$eHAwK&jH4%CT(D=jL=2vOQsN&eO__wO2mAJ0s2$3_ktSVZve$BskwueluDSA!a`!-K zY$<;fY;MfPCD@T?ssgw-rkPR0*cVj0YZqJ%`~lbeK=7$q_2^vsa?ruT*pt_nhWYnv zI!Q=AvZQO-tY_=4?>xWPMnl1YL800YgJ5zc^L3_q7&3@ z5UI+)JgM8B_G)UN6lDl*`K!$}z;Mjm88k2GhLjE1bn3Fyz%Nra(^jqh{PgNdlCzbV zO;z?PYpbiPa0gFBJL~JWhB7Fi-@qa%(%RfOp1}Vq!OYj!%tCd$6>g`sfe&)WPi?BX zad74$fwjt*W)SwA3*aFQ|U3tTL7YWjIa+3~Zd4as~ljdqQ|f6BMiqvVc=k z92hD_c7Pk`KS%`@sB18ae0^{a?zM;G`XFWx^5~77<4=|(*s)L)+3WD02rGF!q|ogI zW)wN@BS}GM)}$a~k-ylC13DJhlQ8`7UmC&&;E<1zA3p$mGXjW5;M^jpIMKxSI0ti} zA7fsQD(qPc0`}oOnDN9sF!3RU+}A-pL`Vw+_~{`dM%)F7cXiG5$G^;uk1_w&&_}3M zR%-V0FkuBi*a-&&4o;?F?!S1dN=h)n^@iChoi})AOky6VS=j!5SlsLXG-DI;wXNz)6c3+5%&N zKnE?=R|Mct$&L8D(%NZM!DI~nlYFh<@dFJ&n~xK#X`q<%iC#uZt-(8z`1 zcfX+ltNg8Vgopv`eH-yE%zYj4E^II`Xf$ekN$l1&YeFITgMLC^*!$KW(&~S5%FzAa zCK>XHv0z=&iLzi_%Kzq+p%t$GapV!KOE%FKtV=cV6bvAiDj?SRk5fhmqDwB(E*L;E zkrxbLnCJ@zP)&db^OH}E1oM+llmzorP8QzD0o-yFE57F7V3Z|#&_Q(S;j49Nav(cIMSWsy3r=xDjjXK5ncN5$ZHN#Ju-x3 zZJC-o695(DI&*0Rv83cq8JUv?4O>!Dj1`2h?)5{8v1SV-dAyn{_^3q-io|WjR%CG6 zdLD|#)UAR`Mv&8o0Mvb@fod>5ToktPaBo(Ma2Dwmlf)fAS_9jJCHjGd;1r(|$r2xl zO3-lp9g(wsuu4y)V46_Up;7g`Q($K3_AvDhe3161L*-v5zS~2m11iZ8g?e})FZ+)p zpIep05F!Ugf4GPE7Rp0#-xH$6bw(W(WrisonYYSESXKp>rstu1^&7x=VS-5XYu zt=(F&w5ckz^R5JK)T+%!=P1ZD-=%;U03jkXoWBHiZ3i9*D10(o@hu`W1aho* zd^_5C)~j1onpaio9`9l`WUCie1*Un~*0Xj(gW4(9vo=Bta2?%$|1i@1({BT+)30Xl z3U2Z;92aa~oIk9;w4FRcmhZ)BgHa>(yLn=*WwBC~8+=yGfcKoWcdkPE2L98nF0+(Q zS`nP(6dDNZ%xfEJBdy8QXEWB5aJ}UE*1>dPcMjre+CVG z`+yhsIz{oR;bTT;vf^OQl|+FsSt6#dPJid{ZK#AIkuoJiK1scfnT4Un74wUs@&#kjo)m)FE#X!~VN zQG%q2LF0mJ$RFzsR!Zy;;=~MH3Ub5Fek+lzh|dj?2gw~hSmY@R!yIjFf&g7OanSeF>=AG6r;!%SHC@^s$+tEG0D{kO#I_VdH-SrUy zk0H>RMa1kNlHALJKw3y?JG*sd0L`D8cgR9eF72AMIk3~ci^SGVyMF`K9wC)+{?z~N zI_MnPcrZ3jMZum@Uge5xhiji9tJ&+((0x(S2lU^0e=m6XdVJ6i1(j$@oPAC98$aO$ z3kXgF8-=6}fi|Y}4b5td_>_2q0f8DYBVrk@Vt#n@fGD5vysDMia6C0yA1OtA{PGMz z00%nqvU4RiHZRi6wJy>HqM}Xp+lIszpeaID9UJdfS@QVZtSk4I>J|gp3ijcdyLCq# z)>6&3Ym=5EF_B8AzFJ1@obGdey?x+m5-}nuyLYQUH2c;49_F?y^>>Q|{Z77X0GT0k z9NLpGL2IN`J~?9$e4D$-DKcq~T961_0e8)pe$>l_XS%P_KSAv)JzeuijQJz{>&}Z_ zSlqDzt~TORys4^P8RUvt(|7lSQ$wmC94o+S$<6N+7|xAxkJvsnK8(#|9G0{EM%Bv9zV zwLs>6S*motn}vgXQ0H73=3v5&&xpy53bEQO8auY?e%Ks~j-vAhx*Xh34DW_$m0m7F zcUTE@tE9W`WVWKKZVh9(#C1s2QO~ARDQ|&TUymPSh!MnzsV&mdQ4;NDIc3j@S;=v< zu*aR<##`j9gJjm28@xlK`MJm4J>idj^S7?q&+D3DCN8JSRHOSyXPIJ`MCP}-YmQ!X z%?9JEyM8mCCR(xSdSP-$i78ha!efY^Og~xAQBS2QSDsIO#nqGmb$4iCYQ4qMKb!hl zy4V{wlGd6ZYb_9T4ID*qXkDyQMknh&)K(x=zaMo^Fm$=OWIllH+zGMBahuea>JgL) z(uwdvj0G)WstU~{_sIvQIirkfPrO+$@NsbD)0GOZQ$}I3j`o}X3Fmeb_~+p^|ET28 z2UFpPU2M)C7LC9{@Dq^Yul$aP!H6c4N}mE0H@J+zB)l9r8n{9!Q{`9a3`CA9arHAU z>`&@OIJOhFDV9}l(IbVZ4`|Y5dDHG+sejA&yy*CL|B)xrZa>vV<&QiP->okBCMBLd zpyY%8z9e3pbIC)b3ey_yiRM2|+_R9X`D+ju^rwOa!WN|I6im?g{>Ia!z^i^z;8|U= zZqL?q3-P79ugdEIjV(~J*Ux_9@d77t4NFUVk16UJ!04)T7!nNHHIU5MN2gF5NV!Rn z>8|`erq?)12zeSGwUA6yM6sr@nJg>K)odn2FLqboWaj z+O1p!H`I>J5Vlh$FDx=8MiPU}9K1IRbR%-mnk6zP(uGzL&Jv~W0b|j`HRnr2hG(-| zYDh5UlA=BPw-nGA;?LsY1{Q-=MjX;0sv)=ltC>55yto;Y8Y|J5xjR_~%|#;WiYq*ijkT>0u(zm!(eR_&D zH-L?v!t9U7|DE45E z{kA;d)#~~x_uY!pPNSBO4S)({%z3vzU?jdyA%54N^DA~OFy5|cU0KqFUsf=ct^=sN zr@IM}%kKTN<_~I+DGm~nI}D&6pW!8tf{446kR&8w-K`y}hGz&%J1hQPrXKN5FNU)! zLMFR_)W2j|f{%VGPQD)i$q{Zz7wj6Vhh<@k=6RAF(}|#FlDp1u1nu+=dU1`KDf$lzS?fS@+5!SOP{rg|nKv`GoC?QZDZ* zcu6P{5_q|9dAZYeUKN1ROAU0XXGU~HI+TK6@x;_;T|t>l)(g~%Zq@CH_zAV^tcH>Y ziAop=mZM(J#&r6lMnoS<6cBXy`()g}wB8C(!6owT0(q+9mX@q;D0(&Hh&Dsm-%Bg^zyY3b)2DI9m8A>nym>!whQWfCa7pfzP^w zacCGMz=R^1=k(cw{o{SQ=xHPDS8;?9gcJ1dw=G7S4okXn5t;^q1rpB)1hL*Y7^3Ua z8zT^`ok#(MsjLZ9jN75MJXn7I-$Qh~ZPO8UGjx^b^F& zCBL9Bh0$U1_;&_WTtO(|k^2>92XMo5qeUnegD@-vN>?)TT7Ufnv`UWK_SH;BlPj*{3)M_0D2x$Hg-%cTTqGJT^ z3>`!P{l&}(_Gm6ZX`K-IU + '$scope', 'auth', '$location', 'upload', '$state', '$stateParams', '$injector', '$rootScope' + ($scope, auth, $location, upload, $state, $stateParams, $injector, $rootScope) -> $scope.auth = auth.data $scope.$state = $state $scope.$stateParams = $stateParams $loadingElement = null + $rootScope.safeApply = (fn) -> + phase = $rootScope.$$phase + if (phase == '$apply' || phase == 'digest') + fn() + return + + $rootScope.$apply fn + $scope.logout = () -> auth.logout().done -> location.reload() diff --git a/public/scripts/app/controllers/artist-favourites.coffee b/public/scripts/app/controllers/artist-favourites.coffee index f2b1792a..04a78aa0 100644 --- a/public/scripts/app/controllers/artist-favourites.coffee +++ b/public/scripts/app/controllers/artist-favourites.coffee @@ -1,12 +1,13 @@ window.pfm.preloaders['artist-favourites'] = [ 'artists', '$state' (artists, $state) -> - artists.fetch $state.params.slug, true + artists.fetchFavourites $state.params.slug, true ] angular.module('ponyfm').controller "artist-favourites", [ '$scope', 'artists', '$state' ($scope, artists, $state) -> - artists.fetch($state.params.slug).done (artistResponse) -> - $scope.artist = artistResponse.artist + artists.fetchFavourites($state.params.slug).done (artistResponse) -> + $scope.favourites = artistResponse + console.log artistResponse ] \ No newline at end of file diff --git a/public/scripts/app/controllers/playlist-form.coffee b/public/scripts/app/controllers/playlist-form.coffee index c955d98b..ba2e6a4a 100644 --- a/public/scripts/app/controllers/playlist-form.coffee +++ b/public/scripts/app/controllers/playlist-form.coffee @@ -16,12 +16,12 @@ angular.module('ponyfm').controller "playlist-form", [ playlists.editPlaylist($scope.form) def - .done -> - dialog.close() + .done (res) -> + dialog.close(res) .fail (errors)-> $scope.errors = errors $scope.isLoading = false - $scope.close = () -> dialog.close() + $scope.close = () -> dialog.close(null) ] diff --git a/public/scripts/app/controllers/playlist.coffee b/public/scripts/app/controllers/playlist.coffee index 8fc97e7f..f13a7bff 100644 --- a/public/scripts/app/controllers/playlist.coffee +++ b/public/scripts/app/controllers/playlist.coffee @@ -1,11 +1,12 @@ -angular.module('ponyfm').controller 'playlist', [ - '$scope', '$state' - ($scope, $state) -> - console.log $state.params.id - $scope.refresh = () -> - $.getJSON('/api/web/playlists/show/' + $state.params.id) - .done (playlist) -> $scope.$apply -> - $scope.playlist = playlist +window.pfm.preloaders['playlist'] = [ + '$state', 'playlists' + ($state, playlists) -> + playlists.fetch $state.params.id, true +] - $scope.refresh() +angular.module('ponyfm').controller 'playlist', [ + '$scope', '$state', 'playlists' + ($scope, $state, playlists) -> + playlists.fetch($state.params.id).done (playlist) -> + $scope.playlist = playlist ] \ No newline at end of file diff --git a/public/scripts/app/controllers/track.coffee b/public/scripts/app/controllers/track.coffee index 35475ab0..3f953775 100644 --- a/public/scripts/app/controllers/track.coffee +++ b/public/scripts/app/controllers/track.coffee @@ -5,14 +5,47 @@ window.pfm.preloaders['track'] = [ ] angular.module('ponyfm').controller "track", [ - '$scope', 'tracks', '$state', 'playlists', 'auth' - ($scope, tracks, $state, playlists, auth) -> + '$scope', 'tracks', '$state', 'playlists', 'auth', 'favourites', '$dialog' + ($scope, tracks, $state, playlists, auth, favourites, $dialog) -> tracks.fetch($state.params.id).done (trackResponse) -> $scope.track = trackResponse.track + $scope.trackArray = [$scope.track] $scope.playlists = [] if auth.data.isLogged playlists.refreshOwned().done (lists) -> $scope.playlists.push list for list in lists + + $scope.favouriteWorking = false + + $scope.toggleFavourite = (track) -> + $scope.favouriteWorking = true + favourites.toggle('track', track.id).done (res) -> + track.is_favourited = res.is_favourited + $scope.favouriteWorking = false + + $scope.addToNewPlaylist = () -> + dialog = $dialog.dialog + templateUrl: '/templates/partials/playlist-dialog.html' + controller: 'playlist-form' + resolve: { + playlist: () -> + is_public: true + is_pinned: true + name: '' + description: '' + } + + dialog.open().then (playlist) -> + return if !playlist + + playlists.addTrackToPlaylist playlist.id, $scope.track.id + $state.transitionTo 'playlist', {id: playlist.id} + + $scope.addToPlaylist = (playlist) -> + return if playlist.message + + playlists.addTrackToPlaylist(playlist.id, $scope.track.id).done (res) -> + playlist.message = res.message ] \ No newline at end of file diff --git a/public/scripts/app/directives/albums-list.coffee b/public/scripts/app/directives/albums-list.coffee index 360b06f4..a8287415 100644 --- a/public/scripts/app/directives/albums-list.coffee +++ b/public/scripts/app/directives/albums-list.coffee @@ -6,6 +6,7 @@ angular.module('ponyfm').directive 'pfmAlbumsList', () -> class: '@class' controller: [ - '$scope' - ($scope) -> + '$scope', 'auth' + ($scope, auth) -> + $scope.auth = auth.data ] \ No newline at end of file diff --git a/public/scripts/app/directives/comments.coffee b/public/scripts/app/directives/comments.coffee new file mode 100644 index 00000000..393945ea --- /dev/null +++ b/public/scripts/app/directives/comments.coffee @@ -0,0 +1,29 @@ +angular.module('ponyfm').directive 'pfmComments', () -> + restrict: 'E' + templateUrl: '/templates/directives/comments.html' + scope: + resource: '=resource', + type: '@type' + + controller: [ + '$scope', 'comments' + ($scope, comments, auth) -> + + $scope.isWorking = false + $scope.content = '' + $scope.auth = auth.data + + refresh = () -> + comments.fetchList($scope.type, $scope.resource.id, true).done (comments) -> + $scope.resource.comments.count = comments.count + $scope.resource.comments.list.length = 0 + $scope.resource.comments.list.push comment for comment in comments.list + $scope.isWorking = false + + $scope.addComment = () -> + content = $scope.content + $scope.content = '' + $scope.isWorking = true + comments.addComment($scope.type, $scope.resource.id, content).done () -> + refresh() + ] \ No newline at end of file diff --git a/public/scripts/app/directives/favouriteButton.coffee b/public/scripts/app/directives/favouriteButton.coffee new file mode 100644 index 00000000..2956ceea --- /dev/null +++ b/public/scripts/app/directives/favouriteButton.coffee @@ -0,0 +1,20 @@ +angular.module('ponyfm').directive 'pfmFavouriteButton', () -> + restrict: 'E' + templateUrl: '/templates/directives/favourite-button.html' + scope: + resource: '=resource', + class: '@class', + type: '@type' + + controller: [ + '$scope', 'favourites', 'auth' + ($scope, favourites, auth) -> + $scope.auth = auth.data + + $scope.isWorking = false + $scope.toggleFavourite = () -> + $scope.isWorking = true + favourites.toggle($scope.type, $scope.resource.id).done (res) -> + $scope.isWorking = false + $scope.resource.is_favourited = res.is_favourited + ] \ No newline at end of file diff --git a/public/scripts/app/directives/player.coffee b/public/scripts/app/directives/player.coffee new file mode 100644 index 00000000..e1bf6c26 --- /dev/null +++ b/public/scripts/app/directives/player.coffee @@ -0,0 +1,73 @@ +angular.module('ponyfm').directive 'pfmPlayer', () -> + $element = null + + restrict: 'E' + templateUrl: '/templates/directives/player.html' + scope: {} + + compile: (element) -> + $element = element + + controller: [ + '$scope', 'player', 'auth' + ($scope, player, auth) -> + $scope.player = player + $scope.auth = auth.data + $scope.playPause = () -> + $scope.player.playPause() + + $scope.playNext = () -> + $scope.player.playNext() + + $scope.playPrev = () -> + $scope.player.playPrev() + + $scope.seek = (e) -> + $transport = $ '.transport' + percent = ((e.pageX - $transport.offset().left) / $transport.width()) + duration = parseFloat($scope.player.currentTrack.duration) + + $scope.player.seek percent * duration * 1000 + + isSliding = false + $slider = $element.find('.volume-slider') + $knob = $element.find('.volume-slider .knob') + $bar = $element.find('.volume-slider .bar') + + player.readyDef.done -> + initialY = (180 - (180 * (player.volume / 100))) - 7.5 + $knob.css {top: initialY} + + moveVolumeSlider = (absoluteY) -> + newY = absoluteY - $bar.offset().top; + maxY = $bar.height() - ($knob.height() / 2) + + newY = 0 if newY < 0 + newY = maxY if newY > maxY + + percent = 100 - ((newY / maxY) * 100) + $scope.player.setVolume percent + $knob.css {top: newY} + + $knob.click (e) -> + e.preventDefault() + e.stopPropagation() + + $slider.click (e) -> $scope.$apply -> moveVolumeSlider(e.pageY) + + $(document).mousemove (e) -> + return if !isSliding + moveVolumeSlider(e.pageY) + + $knob.mousedown (e) -> + e.preventDefault() + e.stopPropagation() + isSliding = true + $slider.parent().addClass('keep-open') + + $(document).mouseup (e) -> + e.preventDefault() + e.stopPropagation() + isSliding = false + $slider.parent().removeClass('keep-open') + ] \ No newline at end of file diff --git a/public/scripts/app/directives/progress-bar.coffee b/public/scripts/app/directives/progress-bar.coffee index 31c26185..7443035e 100644 --- a/public/scripts/app/directives/progress-bar.coffee +++ b/public/scripts/app/directives/progress-bar.coffee @@ -2,4 +2,4 @@ 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 + $(element).css 'width', val + '%' \ No newline at end of file diff --git a/public/scripts/app/directives/tracks-list.coffee b/public/scripts/app/directives/tracks-list.coffee index 4a43a57d..f132708d 100644 --- a/public/scripts/app/directives/tracks-list.coffee +++ b/public/scripts/app/directives/tracks-list.coffee @@ -6,6 +6,15 @@ angular.module('ponyfm').directive 'pfmTracksList', () -> class: '@class' controller: [ - '$scope' - ($scope) -> + '$scope', 'favourites', 'player', 'auth' + ($scope, favourites, player, auth) -> + $scope.auth = auth.data + + $scope.toggleFavourite = (track) -> + favourites.toggle('track', track.id).done (res) -> + track.is_favourited = res.is_favourited + + $scope.play = (track) -> + index = _.indexOf $scope.tracks, (t) -> t.id == track.id + player.playTracks $scope.tracks, index ] \ No newline at end of file diff --git a/public/scripts/app/filters/newlines.coffee b/public/scripts/app/filters/newlines.coffee index 37ecfc8d..38f18ace 100644 --- a/public/scripts/app/filters/newlines.coffee +++ b/public/scripts/app/filters/newlines.coffee @@ -1,2 +1,4 @@ angular.module('ponyfm').filter 'newlines', () -> - (input) -> input.replace(/\n/g, '
    ') \ No newline at end of file + (input) -> + return '' if !input + input.replace(/\n/g, '
    ') \ No newline at end of file diff --git a/public/scripts/app/filters/noHTML.coffee b/public/scripts/app/filters/noHTML.coffee index c5add856..b00fd36f 100644 --- a/public/scripts/app/filters/noHTML.coffee +++ b/public/scripts/app/filters/noHTML.coffee @@ -1,5 +1,7 @@ angular.module('ponyfm').filter 'noHTML', () -> (input) -> + return '' if !input + input.replace(/&/g, '&') .replace(/>/g, '>') .replace(/ + force = force || false + slug = 1 if !slug + return artistFavourites[slug] if !force && artistFavourites[slug] + artistsDef = new $.Deferred() + $http.get('/api/web/artists/' + slug + '/favourites').success (albums) -> + artistsDef.resolve albums + + artistFavourites[slug] = artistsDef.promise() + self ]) \ No newline at end of file diff --git a/public/scripts/app/services/comments.coffee b/public/scripts/app/services/comments.coffee new file mode 100644 index 00000000..0033f1a1 --- /dev/null +++ b/public/scripts/app/services/comments.coffee @@ -0,0 +1,27 @@ +angular.module('ponyfm').factory('comments', [ + '$rootScope', '$http' + ($rootScope, $http) -> + commentCache = [] + + self = + filters: {} + + addComment: (resourceType, resourceId, content) -> + commentDef = new $.Deferred() + $http.post('/api/web/comments/' + resourceType + '/' + resourceId, {content: content, _token: pfm.token}).success (comment) -> + commentDef.resolve comment + + commentDef.promise() + + fetchList: (resourceType, resourceId, force) -> + key = resourceType + '-' + resourceId + force = force || false + return commentCache[key] if !force && commentCache[key] + commentDef = new $.Deferred() + $http.get('/api/web/comments/' + resourceType + '/' + resourceId).success (comments) -> + commentDef.resolve comments + + commentCache[key] = commentDef.promise() + + self +]) \ No newline at end of file diff --git a/public/scripts/app/services/favourites.coffee b/public/scripts/app/services/favourites.coffee new file mode 100644 index 00000000..1165bde7 --- /dev/null +++ b/public/scripts/app/services/favourites.coffee @@ -0,0 +1,13 @@ +angular.module('ponyfm').factory('favourites', [ + '$rootScope', '$http' + ($rootScope, $http) -> + self = + toggle: (type, id) -> + def = new $.Deferred() + $http.post('/api/web/favourites/toggle', {type: type, id: id, _token: pfm.token}).success (res) -> + def.resolve res + + def.promise() + + self +]) \ No newline at end of file diff --git a/public/scripts/app/services/player.coffee b/public/scripts/app/services/player.coffee new file mode 100644 index 00000000..2335cd26 --- /dev/null +++ b/public/scripts/app/services/player.coffee @@ -0,0 +1,117 @@ +angular.module('ponyfm').factory('player', [ + '$rootScope' + ($rootScope) -> + readyDef = new $.Deferred() + + play = (track) -> + self.currentTrack = track + $rootScope.$broadcast 'player-starting-track', track + self.currentSound = soundManager.createSound + url: '/t' + track.id + '/stream', + volume: self.volume + + whileloading: () -> $rootScope.safeApply -> + track.loadingProgress = (self.currentSound.bytesLoaded / self.currentSound.bytesTotal) * 100 + + whileplaying: () -> $rootScope.safeApply -> + track.progress = (self.currentSound.position / (track.duration * 1000)) * 100 + + onfinish: () -> $rootScope.safeApply -> + track.isPlaying = false + self.playNext() + + onstop: () -> $rootScope.safeApply -> + track.isPlaying = false + self.isPlaying = false + + onplay: () -> $rootScope.safeApply -> + track.isPlaying = true + + onresume: () -> $rootScope.safeApply -> + track.isPlaying = true + + onpause: () -> $rootScope.safeApply -> + track.isPlaying = false + + track.isPlaying = true + self.isPlaying = true + self.currentSound.play() + + self = + ready: false + isPlaying: false + currentTrack: null + currentSound: null + playlist: [] + playlistIndex: 0 + volume: 0 + readyDef: readyDef.promise() + + playPause: () -> + return if !self.ready + return if !self.isPlaying + + if self.currentSound.paused + self.currentSound.play() + else + self.currentSound.pause() + + playNext: () -> + self.currentSound.stop() if self.currentSound != null + self.playlistIndex++ + if self.playlistIndex >= self.playlist.length + self.playlist.length = 0 + self.currentTrack = null + self.currentSong = null + self.isPlaying = false + return + + play self.playlist[self.playlistIndex] + + playPrev: () -> + self.currentSound.stop() if self.currentSound != null + self.playlistIndex-- + if self.playlistIndex <= 0 + self.playlist.length = 0 + self.currentTrack = null + self.currentSong = null + self.isPlaying = false + return + + play self.playlist[self.playlistIndex] + + seek: (progress) -> + return if !self.currentSound + self.currentSound.setPosition(progress) + + setVolume: (theVolume) -> + theVolume = 100 if theVolume > 100 + self.currentSound.setVolume(theVolume) if self.currentSound + $.cookie('pfm-volume', theVolume) + self.volume = theVolume + + playTracks: (tracks, index) -> + return if !self.ready + return if tracks.length == 0 + + if tracks[index].isPlaying + self.playPause() + return + + self.currentSound.stop() if self.currentSound != null + + $rootScope.$broadcast 'player-stopping-playlist' + self.playlist.length = 0 + self.playlist.push track for track in tracks + self.playlistIndex = index + + $rootScope.$broadcast 'player-starting-playlist', tracks + play tracks[index] + + pfm.soundManager.done () -> + self.ready = true + self.setVolume($.cookie('pfm-volume') || 100) + readyDef.resolve() + + self +]) \ No newline at end of file diff --git a/public/scripts/app/services/playlists.coffee b/public/scripts/app/services/playlists.coffee index bf1b1529..ab7e2689 100644 --- a/public/scripts/app/services/playlists.coffee +++ b/public/scripts/app/services/playlists.coffee @@ -2,9 +2,20 @@ angular.module('ponyfm').factory('playlists', [ '$rootScope', '$state', '$http' ($rootScope, $state, $http) -> playlistDef = null + playlists = {} self = pinnedPlaylists: [] + + fetch: (id, force) -> + force = force || false + return playlists[id] if !force && playlists[id] + def = new $.Deferred() + $http.get('/api/web/playlists/' + id).success (playlist) -> + def.resolve playlist + + playlists[id] = def.promise() + refreshOwned: (force) -> force = force || false return playlistDef if !force && playlistDef @@ -15,6 +26,13 @@ angular.module('ponyfm').factory('playlists', [ playlistDef + addTrackToPlaylist: (playlistId, trackId) -> + def = new $.Deferred() + $http.post('/api/web/playlists/' + playlistId + '/add-track', {track_id: trackId, _token: pfm.token}).success (res) -> + def.resolve(res) + + def + refresh: () -> $.getJSON('/api/web/playlists/pinned') .done (playlists) -> $rootScope.$apply -> diff --git a/public/scripts/base/jquery.cookie.js b/public/scripts/base/jquery.cookie.js new file mode 100644 index 00000000..01cde6f9 --- /dev/null +++ b/public/scripts/base/jquery.cookie.js @@ -0,0 +1,95 @@ +/*! + * jQuery Cookie Plugin v1.3.1 + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2013 Klaus Hartl + * Released under the MIT license + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define(['jquery'], factory); + } else { + // Browser globals. + factory(jQuery); + } +}(function ($) { + + var pluses = /\+/g; + + function raw(s) { + return s; + } + + function decoded(s) { + return decodeURIComponent(s.replace(pluses, ' ')); + } + + function converted(s) { + if (s.indexOf('"') === 0) { + // This is a quoted cookie as according to RFC2068, unescape + s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } + try { + return config.json ? JSON.parse(s) : s; + } catch(er) {} + } + + var config = $.cookie = function (key, value, options) { + + // write + if (value !== undefined) { + options = $.extend({}, config.defaults, options); + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = config.json ? JSON.stringify(value) : String(value); + + return (document.cookie = [ + config.raw ? key : encodeURIComponent(key), + '=', + config.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // read + var decode = config.raw ? raw : decoded; + var cookies = document.cookie.split('; '); + var result = key ? undefined : {}; + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = decode(parts.join('=')); + + if (key && key === name) { + result = converted(cookie); + break; + } + + if (!key) { + result[name] = converted(cookie); + } + } + + return result; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) !== undefined) { + // Must not alter options, thus extending a fresh object... + $.cookie(key, '', $.extend({}, options, { expires: -1 })); + return true; + } + return false; + }; + +})); \ No newline at end of file diff --git a/public/scripts/base/soundmanager2-nodebug.js b/public/scripts/base/soundmanager2-nodebug.js new file mode 100644 index 00000000..8d7c2472 --- /dev/null +++ b/public/scripts/base/soundmanager2-nodebug.js @@ -0,0 +1,2643 @@ +/** @license + * + * SoundManager 2: JavaScript Sound for the Web + * ---------------------------------------------- + * http://schillmania.com/projects/soundmanager2/ + * + * Copyright (c) 2007, Scott Schiller. All rights reserved. + * Code provided under the BSD License: + * http://schillmania.com/projects/soundmanager2/license.txt + * + * V2.97a.20130512 + */ + +/*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio, opera */ +/*jslint regexp: true, sloppy: true, white: true, nomen: true, plusplus: true, todo: true */ + +(function(window, _undefined) { +"use strict"; +var soundManager = null; +function SoundManager(smURL, smID) { + this.setupOptions = { + 'url': (smURL || null), + 'flashVersion': 8, + 'debugMode': true, + 'debugFlash': false, + 'useConsole': true, + 'consoleOnly': true, + 'waitForWindowLoad': false, + 'bgColor': '#ffffff', + 'useHighPerformance': false, + 'flashPollingInterval': null, + 'html5PollingInterval': null, + 'flashLoadTimeout': 1000, + 'wmode': null, + 'allowScriptAccess': 'always', + 'useFlashBlock': false, + 'useHTML5Audio': true, + 'html5Test': /^(probably|maybe)$/i, + 'preferFlash': true, + 'noSWFCache': false, + 'idPrefix': 'sound' + }; + this.defaultOptions = { + 'autoLoad': false, + 'autoPlay': false, + 'from': null, + 'loops': 1, + 'onid3': null, + 'onload': null, + 'whileloading': null, + 'onplay': null, + 'onpause': null, + 'onresume': null, + 'whileplaying': null, + 'onposition': null, + 'onstop': null, + 'onfailure': null, + 'onfinish': null, + 'multiShot': true, + 'multiShotEvents': false, + 'position': null, + 'pan': 0, + 'stream': true, + 'to': null, + 'type': null, + 'usePolicyFile': false, + 'volume': 100 + }; + this.flash9Options = { + 'isMovieStar': null, + 'usePeakData': false, + 'useWaveformData': false, + 'useEQData': false, + 'onbufferchange': null, + 'ondataerror': null + }; + this.movieStarOptions = { + 'bufferTime': 3, + 'serverURL': null, + 'onconnect': null, + 'duration': null + }; + this.audioFormats = { + 'mp3': { + 'type': ['audio/mpeg; codecs="mp3"', 'audio/mpeg', 'audio/mp3', 'audio/MPA', 'audio/mpa-robust'], + 'required': true + }, + 'mp4': { + 'related': ['aac','m4a','m4b'], + 'type': ['audio/mp4; codecs="mp4a.40.2"', 'audio/aac', 'audio/x-m4a', 'audio/MP4A-LATM', 'audio/mpeg4-generic'], + 'required': false + }, + 'ogg': { + 'type': ['audio/ogg; codecs=vorbis'], + 'required': false + }, + 'opus': { + 'type': ['audio/ogg; codecs=opus', 'audio/opus'], + 'required': false + }, + 'wav': { + 'type': ['audio/wav; codecs="1"', 'audio/wav', 'audio/wave', 'audio/x-wav'], + 'required': false + } + }; + this.movieID = 'sm2-container'; + this.id = (smID || 'sm2movie'); + this.debugID = 'soundmanager-debug'; + this.debugURLParam = /([#?&])debug=1/i; + this.versionNumber = 'V2.97a.20130512'; + this.version = null; + this.movieURL = null; + this.altURL = null; + this.swfLoaded = false; + this.enabled = false; + this.oMC = null; + this.sounds = {}; + this.soundIDs = []; + this.muted = false; + this.didFlashBlock = false; + this.filePattern = null; + this.filePatterns = { + 'flash8': /\.mp3(\?.*)?$/i, + 'flash9': /\.mp3(\?.*)?$/i + }; + this.features = { + 'buffering': false, + 'peakData': false, + 'waveformData': false, + 'eqData': false, + 'movieStar': false + }; + this.sandbox = { + }; + this.html5 = { + 'usingFlash': null + }; + this.flash = {}; + this.html5Only = false; + this.ignoreFlash = false; + var SMSound, + sm2 = this, globalHTML5Audio = null, flash = null, sm = 'soundManager', smc = sm + ': ', h5 = 'HTML5::', id, ua = navigator.userAgent, wl = window.location.href.toString(), doc = document, doNothing, setProperties, init, fV, on_queue = [], debugOpen = true, debugTS, didAppend = false, appendSuccess = false, didInit = false, disabled = false, windowLoaded = false, _wDS, wdCount = 0, initComplete, mixin, assign, extraOptions, addOnEvent, processOnEvents, initUserOnload, delayWaitForEI, waitForEI, setVersionInfo, handleFocus, strings, initMovie, preInit, domContentLoaded, winOnLoad, didDCLoaded, getDocument, createMovie, catchError, setPolling, initDebug, debugLevels = ['log', 'info', 'warn', 'error'], defaultFlashVersion = 8, disableObject, failSafely, normalizeMovieURL, oRemoved = null, oRemovedHTML = null, str, flashBlockHandler, getSWFCSS, swfCSS, toggleDebug, loopFix, policyFix, complain, idCheck, waitingForEI = false, initPending = false, startTimer, stopTimer, timerExecute, h5TimerCount = 0, h5IntervalTimer = null, parseURL, messages = [], + canIgnoreFlash, needsFlash = null, featureCheck, html5OK, html5CanPlay, html5Ext, html5Unload, domContentLoadedIE, testHTML5, event, slice = Array.prototype.slice, useGlobalHTML5Audio = false, lastGlobalHTML5URL, hasFlash, detectFlash, badSafariFix, html5_events, showSupport, flushMessages, wrapCallback, idCounter = 0, + is_iDevice = ua.match(/(ipad|iphone|ipod)/i), isAndroid = ua.match(/android/i), isIE = ua.match(/msie/i), isWebkit = ua.match(/webkit/i), isSafari = (ua.match(/safari/i) && !ua.match(/chrome/i)), isOpera = (ua.match(/opera/i)), isFirefox = (ua.match(/firefox/i)), + mobileHTML5 = (ua.match(/(mobile|pre\/|xoom)/i) || is_iDevice || isAndroid), + isBadSafari = (!wl.match(/usehtml5audio/i) && !wl.match(/sm2\-ignorebadua/i) && isSafari && !ua.match(/silk/i) && ua.match(/OS X 10_6_([3-7])/i)), + hasConsole = (window.console !== _undefined && console.log !== _undefined), isFocused = (doc.hasFocus !== _undefined?doc.hasFocus():null), tryInitOnFocus = (isSafari && (doc.hasFocus === _undefined || !doc.hasFocus())), okToDisable = !tryInitOnFocus, flashMIME = /(mp3|mp4|mpa|m4a|m4b)/i, msecScale = 1000, + emptyURL = 'about:blank', + overHTTP = (doc.location?doc.location.protocol.match(/http/i):null), + http = (!overHTTP ? 'http:/'+'/' : ''), + netStreamMimeTypes = /^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/i, + netStreamTypes = ['mpeg4', 'aac', 'flv', 'mov', 'mp4', 'm4v', 'f4v', 'm4a', 'm4b', 'mp4v', '3gp', '3g2'], + netStreamPattern = new RegExp('\\.(' + netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + this.mimePattern = /^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i; + this.useAltURL = !overHTTP; + swfCSS = { + 'swfBox': 'sm2-object-box', + 'swfDefault': 'movieContainer', + 'swfError': 'swf_error', + 'swfTimedout': 'swf_timedout', + 'swfLoaded': 'swf_loaded', + 'swfUnblocked': 'swf_unblocked', + 'sm2Debug': 'sm2_debug', + 'highPerf': 'high_performance', + 'flashDebug': 'flash_debug' + }; + this.hasHTML5 = (function() { + try { + return (Audio !== _undefined && (isOpera && opera !== _undefined && opera.version() < 10 ? new Audio(null) : new Audio()).canPlayType !== _undefined); + } catch(e) { + return false; + } + }()); + this.setup = function(options) { + var noURL = (!sm2.url); + if (options !== _undefined && didInit && needsFlash && sm2.ok() && (options.flashVersion !== _undefined || options.url !== _undefined || options.html5Test !== _undefined)) { + } + assign(options); + if (options) { + if (noURL && didDCLoaded && options.url !== _undefined) { + sm2.beginDelayedInit(); + } + if (!didDCLoaded && options.url !== _undefined && doc.readyState === 'complete') { + setTimeout(domContentLoaded, 1); + } + } + return sm2; + }; + this.ok = function() { + return (needsFlash ? (didInit && !disabled) : (sm2.useHTML5Audio && sm2.hasHTML5)); + }; + this.supported = this.ok; + this.getMovie = function(smID) { + return id(smID) || doc[smID] || window[smID]; + }; + this.createSound = function(oOptions, _url) { + var cs, cs_string, options, oSound = null; + if (!didInit || !sm2.ok()) { + return false; + } + if (_url !== _undefined) { + oOptions = { + 'id': oOptions, + 'url': _url + }; + } + options = mixin(oOptions); + options.url = parseURL(options.url); + if (options.id === undefined) { + options.id = sm2.setupOptions.idPrefix + (idCounter++); + } + if (idCheck(options.id, true)) { + return sm2.sounds[options.id]; + } + function make() { + options = loopFix(options); + sm2.sounds[options.id] = new SMSound(options); + sm2.soundIDs.push(options.id); + return sm2.sounds[options.id]; + } + if (html5OK(options)) { + oSound = make(); + oSound._setup_html5(options); + } else { + if (sm2.html5Only) { + return make(); + } + if (sm2.html5.usingFlash && options.url && options.url.match(/data\:/i)) { + return make(); + } + if (fV > 8) { + if (options.isMovieStar === null) { + options.isMovieStar = !!(options.serverURL || (options.type ? options.type.match(netStreamMimeTypes) : false) || (options.url && options.url.match(netStreamPattern))); + } + } + options = policyFix(options, cs); + oSound = make(); + if (fV === 8) { + flash._createSound(options.id, options.loops||1, options.usePolicyFile); + } else { + flash._createSound(options.id, options.url, options.usePeakData, options.useWaveformData, options.useEQData, options.isMovieStar, (options.isMovieStar?options.bufferTime:false), options.loops||1, options.serverURL, options.duration||null, options.autoPlay, true, options.autoLoad, options.usePolicyFile); + if (!options.serverURL) { + oSound.connected = true; + if (options.onconnect) { + options.onconnect.apply(oSound); + } + } + } + if (!options.serverURL && (options.autoLoad || options.autoPlay)) { + oSound.load(options); + } + } + if (!options.serverURL && options.autoPlay) { + oSound.play(); + } + return oSound; + }; + this.destroySound = function(sID, _bFromSound) { + if (!idCheck(sID)) { + return false; + } + var oS = sm2.sounds[sID], i; + oS._iO = {}; + oS.stop(); + oS.unload(); + for (i = 0; i < sm2.soundIDs.length; i++) { + if (sm2.soundIDs[i] === sID) { + sm2.soundIDs.splice(i, 1); + break; + } + } + if (!_bFromSound) { + oS.destruct(true); + } + oS = null; + delete sm2.sounds[sID]; + return true; + }; + this.load = function(sID, oOptions) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].load(oOptions); + }; + this.unload = function(sID) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].unload(); + }; + this.onPosition = function(sID, nPosition, oMethod, oScope) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].onposition(nPosition, oMethod, oScope); + }; + this.onposition = this.onPosition; + this.clearOnPosition = function(sID, nPosition, oMethod) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].clearOnPosition(nPosition, oMethod); + }; + this.play = function(sID, oOptions) { + var result = null, + overloaded = (oOptions && !(oOptions instanceof Object)); + if (!didInit || !sm2.ok()) { + return false; + } + if (!idCheck(sID, overloaded)) { + if (!overloaded) { + return false; + } + if (overloaded) { + oOptions = { + url: oOptions + }; + } + if (oOptions && oOptions.url) { + oOptions.id = sID; + result = sm2.createSound(oOptions).play(); + } + } else if (overloaded) { + oOptions = { + url: oOptions + }; + } + if (result === null) { + result = sm2.sounds[sID].play(oOptions); + } + return result; + }; + this.start = this.play; + this.setPosition = function(sID, nMsecOffset) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].setPosition(nMsecOffset); + }; + this.stop = function(sID) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].stop(); + }; + this.stopAll = function() { + var oSound; + for (oSound in sm2.sounds) { + if (sm2.sounds.hasOwnProperty(oSound)) { + sm2.sounds[oSound].stop(); + } + } + }; + this.pause = function(sID) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].pause(); + }; + this.pauseAll = function() { + var i; + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].pause(); + } + }; + this.resume = function(sID) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].resume(); + }; + this.resumeAll = function() { + var i; + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].resume(); + } + }; + this.togglePause = function(sID) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].togglePause(); + }; + this.setPan = function(sID, nPan) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].setPan(nPan); + }; + this.setVolume = function(sID, nVol) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].setVolume(nVol); + }; + this.mute = function(sID) { + var i = 0; + if (sID instanceof String) { + sID = null; + } + if (!sID) { + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].mute(); + } + sm2.muted = true; + } else { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].mute(); + } + return true; + }; + this.muteAll = function() { + sm2.mute(); + }; + this.unmute = function(sID) { + var i; + if (sID instanceof String) { + sID = null; + } + if (!sID) { + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].unmute(); + } + sm2.muted = false; + } else { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].unmute(); + } + return true; + }; + this.unmuteAll = function() { + sm2.unmute(); + }; + this.toggleMute = function(sID) { + if (!idCheck(sID)) { + return false; + } + return sm2.sounds[sID].toggleMute(); + }; + this.getMemoryUse = function() { + var ram = 0; + if (flash && fV !== 8) { + ram = parseInt(flash._getMemoryUse(), 10); + } + return ram; + }; + this.disable = function(bNoDisable) { + var i; + if (bNoDisable === _undefined) { + bNoDisable = false; + } + if (disabled) { + return false; + } + disabled = true; + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + disableObject(sm2.sounds[sm2.soundIDs[i]]); + } + initComplete(bNoDisable); + event.remove(window, 'load', initUserOnload); + return true; + }; + this.canPlayMIME = function(sMIME) { + var result; + if (sm2.hasHTML5) { + result = html5CanPlay({type:sMIME}); + } + if (!result && needsFlash) { + result = (sMIME && sm2.ok() ? !!((fV > 8 ? sMIME.match(netStreamMimeTypes) : null) || sMIME.match(sm2.mimePattern)) : null); + } + return result; + }; + this.canPlayURL = function(sURL) { + var result; + if (sm2.hasHTML5) { + result = html5CanPlay({url: sURL}); + } + if (!result && needsFlash) { + result = (sURL && sm2.ok() ? !!(sURL.match(sm2.filePattern)) : null); + } + return result; + }; + this.canPlayLink = function(oLink) { + if (oLink.type !== _undefined && oLink.type) { + if (sm2.canPlayMIME(oLink.type)) { + return true; + } + } + return sm2.canPlayURL(oLink.href); + }; + this.getSoundById = function(sID, _suppressDebug) { + if (!sID) { + return null; + } + var result = sm2.sounds[sID]; + return result; + }; + this.onready = function(oMethod, oScope) { + var sType = 'onready', + result = false; + if (typeof oMethod === 'function') { + if (!oScope) { + oScope = window; + } + addOnEvent(sType, oMethod, oScope); + processOnEvents(); + result = true; + } else { + throw str('needFunction', sType); + } + return result; + }; + this.ontimeout = function(oMethod, oScope) { + var sType = 'ontimeout', + result = false; + if (typeof oMethod === 'function') { + if (!oScope) { + oScope = window; + } + addOnEvent(sType, oMethod, oScope); + processOnEvents({type:sType}); + result = true; + } else { + throw str('needFunction', sType); + } + return result; + }; + this._writeDebug = function(sText, sTypeOrObject) { + return true; + }; + this._wD = this._writeDebug; + this._debug = function() { + }; + this.reboot = function(resetEvents, excludeInit) { + var i, j, k; + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].destruct(); + } + if (flash) { + try { + if (isIE) { + oRemovedHTML = flash.innerHTML; + } + oRemoved = flash.parentNode.removeChild(flash); + } catch(e) { + } + } + oRemovedHTML = oRemoved = needsFlash = flash = null; + sm2.enabled = didDCLoaded = didInit = waitingForEI = initPending = didAppend = appendSuccess = disabled = useGlobalHTML5Audio = sm2.swfLoaded = false; + sm2.soundIDs = []; + sm2.sounds = {}; + idCounter = 0; + if (!resetEvents) { + for (i in on_queue) { + if (on_queue.hasOwnProperty(i)) { + for (j = 0, k = on_queue[i].length; j < k; j++) { + on_queue[i][j].fired = false; + } + } + } + } else { + on_queue = []; + } + sm2.html5 = { + 'usingFlash': null + }; + sm2.flash = {}; + sm2.html5Only = false; + sm2.ignoreFlash = false; + window.setTimeout(function() { + preInit(); + if (!excludeInit) { + sm2.beginDelayedInit(); + } + }, 20); + return sm2; + }; + this.reset = function() { + return sm2.reboot(true, true); + }; + this.getMoviePercent = function() { + return (flash && 'PercentLoaded' in flash ? flash.PercentLoaded() : null); + }; + this.beginDelayedInit = function() { + windowLoaded = true; + domContentLoaded(); + setTimeout(function() { + if (initPending) { + return false; + } + createMovie(); + initMovie(); + initPending = true; + return true; + }, 20); + delayWaitForEI(); + }; + this.destruct = function() { + sm2.disable(true); + }; + SMSound = function(oOptions) { + var s = this, resetProperties, add_html5_events, remove_html5_events, stop_html5_timer, start_html5_timer, attachOnPosition, onplay_called = false, onPositionItems = [], onPositionFired = 0, detachOnPosition, applyFromTo, lastURL = null, lastHTML5State, urlOmitted; + lastHTML5State = { + duration: null, + time: null + }; + this.id = oOptions.id; + this.sID = this.id; + this.url = oOptions.url; + this.options = mixin(oOptions); + this.instanceOptions = this.options; + this._iO = this.instanceOptions; + this.pan = this.options.pan; + this.volume = this.options.volume; + this.isHTML5 = false; + this._a = null; + urlOmitted = (this.url ? false : true); + this.id3 = {}; + this._debug = function() { + }; + this.load = function(oOptions) { + var oSound = null, instanceOptions; + if (oOptions !== _undefined) { + s._iO = mixin(oOptions, s.options); + } else { + oOptions = s.options; + s._iO = oOptions; + if (lastURL && lastURL !== s.url) { + s._iO.url = s.url; + s.url = null; + } + } + if (!s._iO.url) { + s._iO.url = s.url; + } + s._iO.url = parseURL(s._iO.url); + s.instanceOptions = s._iO; + instanceOptions = s._iO; + if (!instanceOptions.url && !s.url) { + return s; + } + if (instanceOptions.url === s.url && s.readyState !== 0 && s.readyState !== 2) { + if (s.readyState === 3 && instanceOptions.onload) { + wrapCallback(s, function() { + instanceOptions.onload.apply(s, [(!!s.duration)]); + }); + } + return s; + } + s.loaded = false; + s.readyState = 1; + s.playState = 0; + s.id3 = {}; + if (html5OK(instanceOptions)) { + oSound = s._setup_html5(instanceOptions); + if (!oSound._called_load) { + s._html5_canplay = false; + if (s.url !== instanceOptions.url) { + s._a.src = instanceOptions.url; + s.setPosition(0); + } + s._a.autobuffer = 'auto'; + s._a.preload = 'auto'; + s._a._called_load = true; + if (instanceOptions.autoPlay) { + s.play(); + } + } else { + } + } else { + if (sm2.html5Only) { + return s; + } + if (s._iO.url && s._iO.url.match(/data\:/i)) { + return s; + } + try { + s.isHTML5 = false; + s._iO = policyFix(loopFix(instanceOptions)); + instanceOptions = s._iO; + if (fV === 8) { + flash._load(s.id, instanceOptions.url, instanceOptions.stream, instanceOptions.autoPlay, instanceOptions.usePolicyFile); + } else { + flash._load(s.id, instanceOptions.url, !!(instanceOptions.stream), !!(instanceOptions.autoPlay), instanceOptions.loops||1, !!(instanceOptions.autoLoad), instanceOptions.usePolicyFile); + } + } catch(e) { + catchError({type:'SMSOUND_LOAD_JS_EXCEPTION', fatal:true}); + } + } + s.url = instanceOptions.url; + return s; + }; + this.unload = function() { + if (s.readyState !== 0) { + if (!s.isHTML5) { + if (fV === 8) { + flash._unload(s.id, emptyURL); + } else { + flash._unload(s.id); + } + } else { + stop_html5_timer(); + if (s._a) { + s._a.pause(); + lastURL = html5Unload(s._a); + } + } + resetProperties(); + } + return s; + }; + this.destruct = function(_bFromSM) { + if (!s.isHTML5) { + s._iO.onfailure = null; + flash._destroySound(s.id); + } else { + stop_html5_timer(); + if (s._a) { + s._a.pause(); + html5Unload(s._a); + if (!useGlobalHTML5Audio) { + remove_html5_events(); + } + s._a._s = null; + s._a = null; + } + } + if (!_bFromSM) { + sm2.destroySound(s.id, true); + } + }; + this.play = function(oOptions, _updatePlayState) { + var fN, allowMulti, a, onready, + audioClone, onended, oncanplay, + startOK = true, + exit = null; + _updatePlayState = (_updatePlayState === _undefined ? true : _updatePlayState); + if (!oOptions) { + oOptions = {}; + } + if (s.url) { + s._iO.url = s.url; + } + s._iO = mixin(s._iO, s.options); + s._iO = mixin(oOptions, s._iO); + s._iO.url = parseURL(s._iO.url); + s.instanceOptions = s._iO; + if (!s.isHTML5 && s._iO.serverURL && !s.connected) { + if (!s.getAutoPlay()) { + s.setAutoPlay(true); + } + return s; + } + if (html5OK(s._iO)) { + s._setup_html5(s._iO); + start_html5_timer(); + } + if (s.playState === 1 && !s.paused) { + allowMulti = s._iO.multiShot; + if (!allowMulti) { + if (s.isHTML5) { + s.setPosition(s._iO.position); + } + exit = s; + } else { + } + } + if (exit !== null) { + return exit; + } + if (oOptions.url && oOptions.url !== s.url) { + if (!s.readyState && !s.isHTML5 && fV === 8 && urlOmitted) { + urlOmitted = false; + } else { + s.load(s._iO); + } + } + if (!s.loaded) { + if (s.readyState === 0) { + if (!s.isHTML5 && !sm2.html5Only) { + s._iO.autoPlay = true; + s.load(s._iO); + } else if (s.isHTML5) { + s.load(s._iO); + } else { + exit = s; + } + s.instanceOptions = s._iO; + } else if (s.readyState === 2) { + exit = s; + } else { + } + } else { + } + if (exit !== null) { + return exit; + } + if (!s.isHTML5 && fV === 9 && s.position > 0 && s.position === s.duration) { + oOptions.position = 0; + } + if (s.paused && s.position >= 0 && (!s._iO.serverURL || s.position > 0)) { + s.resume(); + } else { + s._iO = mixin(oOptions, s._iO); + if (s._iO.from !== null && s._iO.to !== null && s.instanceCount === 0 && s.playState === 0 && !s._iO.serverURL) { + onready = function() { + s._iO = mixin(oOptions, s._iO); + s.play(s._iO); + }; + if (s.isHTML5 && !s._html5_canplay) { + s.load({ + oncanplay: onready + }); + exit = false; + } else if (!s.isHTML5 && !s.loaded && (!s.readyState || s.readyState !== 2)) { + s.load({ + onload: onready + }); + exit = false; + } + if (exit !== null) { + return exit; + } + s._iO = applyFromTo(); + } + if (!s.instanceCount || s._iO.multiShotEvents || (s.isHTML5 && s._iO.multiShot && !useGlobalHTML5Audio) || (!s.isHTML5 && fV > 8 && !s.getAutoPlay())) { + s.instanceCount++; + } + if (s._iO.onposition && s.playState === 0) { + attachOnPosition(s); + } + s.playState = 1; + s.paused = false; + s.position = (s._iO.position !== _undefined && !isNaN(s._iO.position) ? s._iO.position : 0); + if (!s.isHTML5) { + s._iO = policyFix(loopFix(s._iO)); + } + if (s._iO.onplay && _updatePlayState) { + s._iO.onplay.apply(s); + onplay_called = true; + } + s.setVolume(s._iO.volume, true); + s.setPan(s._iO.pan, true); + if (!s.isHTML5) { + startOK = flash._start(s.id, s._iO.loops || 1, (fV === 9 ? s.position : s.position / msecScale), s._iO.multiShot || false); + if (fV === 9 && !startOK) { + if (s._iO.onplayerror) { + s._iO.onplayerror.apply(s); + } + } + } else { + if (s.instanceCount < 2) { + start_html5_timer(); + a = s._setup_html5(); + s.setPosition(s._iO.position); + a.play(); + } else { + audioClone = new Audio(s._iO.url); + onended = function() { + event.remove(audioClone, 'onended', onended); + s._onfinish(s); + html5Unload(audioClone); + audioClone = null; + }; + oncanplay = function() { + event.remove(audioClone, 'canplay', oncanplay); + try { + audioClone.currentTime = s._iO.position/msecScale; + } catch(err) { + } + audioClone.play(); + }; + event.add(audioClone, 'ended', onended); + if (s._iO.position) { + event.add(audioClone, 'canplay', oncanplay); + } else { + audioClone.play(); + } + } + } + } + return s; + }; + this.start = this.play; + this.stop = function(bAll) { + var instanceOptions = s._iO, + originalPosition; + if (s.playState === 1) { + s._onbufferchange(0); + s._resetOnPosition(0); + s.paused = false; + if (!s.isHTML5) { + s.playState = 0; + } + detachOnPosition(); + if (instanceOptions.to) { + s.clearOnPosition(instanceOptions.to); + } + if (!s.isHTML5) { + flash._stop(s.id, bAll); + if (instanceOptions.serverURL) { + s.unload(); + } + } else { + if (s._a) { + originalPosition = s.position; + s.setPosition(0); + s.position = originalPosition; + s._a.pause(); + s.playState = 0; + s._onTimer(); + stop_html5_timer(); + } + } + s.instanceCount = 0; + s._iO = {}; + if (instanceOptions.onstop) { + instanceOptions.onstop.apply(s); + } + } + return s; + }; + this.setAutoPlay = function(autoPlay) { + s._iO.autoPlay = autoPlay; + if (!s.isHTML5) { + flash._setAutoPlay(s.id, autoPlay); + if (autoPlay) { + if (!s.instanceCount && s.readyState === 1) { + s.instanceCount++; + } + } + } + }; + this.getAutoPlay = function() { + return s._iO.autoPlay; + }; + this.setPosition = function(nMsecOffset) { + if (nMsecOffset === _undefined) { + nMsecOffset = 0; + } + var position, position1K, + offset = (s.isHTML5 ? Math.max(nMsecOffset, 0) : Math.min(s.duration || s._iO.duration, Math.max(nMsecOffset, 0))); + s.position = offset; + position1K = s.position/msecScale; + s._resetOnPosition(s.position); + s._iO.position = offset; + if (!s.isHTML5) { + position = (fV === 9 ? s.position : position1K); + if (s.readyState && s.readyState !== 2) { + flash._setPosition(s.id, position, (s.paused || !s.playState), s._iO.multiShot); + } + } else if (s._a) { + if (s._html5_canplay) { + if (s._a.currentTime !== position1K) { + try { + s._a.currentTime = position1K; + if (s.playState === 0 || s.paused) { + s._a.pause(); + } + } catch(e) { + } + } + } else if (position1K) { + return s; + } + if (s.paused) { + s._onTimer(true); + } + } + return s; + }; + this.pause = function(_bCallFlash) { + if (s.paused || (s.playState === 0 && s.readyState !== 1)) { + return s; + } + s.paused = true; + if (!s.isHTML5) { + if (_bCallFlash || _bCallFlash === _undefined) { + flash._pause(s.id, s._iO.multiShot); + } + } else { + s._setup_html5().pause(); + stop_html5_timer(); + } + if (s._iO.onpause) { + s._iO.onpause.apply(s); + } + return s; + }; + this.resume = function() { + var instanceOptions = s._iO; + if (!s.paused) { + return s; + } + s.paused = false; + s.playState = 1; + if (!s.isHTML5) { + if (instanceOptions.isMovieStar && !instanceOptions.serverURL) { + s.setPosition(s.position); + } + flash._pause(s.id, instanceOptions.multiShot); + } else { + s._setup_html5().play(); + start_html5_timer(); + } + if (!onplay_called && instanceOptions.onplay) { + instanceOptions.onplay.apply(s); + onplay_called = true; + } else if (instanceOptions.onresume) { + instanceOptions.onresume.apply(s); + } + return s; + }; + this.togglePause = function() { + if (s.playState === 0) { + s.play({ + position: (fV === 9 && !s.isHTML5 ? s.position : s.position / msecScale) + }); + return s; + } + if (s.paused) { + s.resume(); + } else { + s.pause(); + } + return s; + }; + this.setPan = function(nPan, bInstanceOnly) { + if (nPan === _undefined) { + nPan = 0; + } + if (bInstanceOnly === _undefined) { + bInstanceOnly = false; + } + if (!s.isHTML5) { + flash._setPan(s.id, nPan); + } + s._iO.pan = nPan; + if (!bInstanceOnly) { + s.pan = nPan; + s.options.pan = nPan; + } + return s; + }; + this.setVolume = function(nVol, _bInstanceOnly) { + if (nVol === _undefined) { + nVol = 100; + } + if (_bInstanceOnly === _undefined) { + _bInstanceOnly = false; + } + if (!s.isHTML5) { + flash._setVolume(s.id, (sm2.muted && !s.muted) || s.muted?0:nVol); + } else if (s._a) { + s._a.volume = Math.max(0, Math.min(1, nVol/100)); + } + s._iO.volume = nVol; + if (!_bInstanceOnly) { + s.volume = nVol; + s.options.volume = nVol; + } + return s; + }; + this.mute = function() { + s.muted = true; + if (!s.isHTML5) { + flash._setVolume(s.id, 0); + } else if (s._a) { + s._a.muted = true; + } + return s; + }; + this.unmute = function() { + s.muted = false; + var hasIO = (s._iO.volume !== _undefined); + if (!s.isHTML5) { + flash._setVolume(s.id, hasIO?s._iO.volume:s.options.volume); + } else if (s._a) { + s._a.muted = false; + } + return s; + }; + this.toggleMute = function() { + return (s.muted?s.unmute():s.mute()); + }; + this.onPosition = function(nPosition, oMethod, oScope) { + onPositionItems.push({ + position: parseInt(nPosition, 10), + method: oMethod, + scope: (oScope !== _undefined ? oScope : s), + fired: false + }); + return s; + }; + this.onposition = this.onPosition; + this.clearOnPosition = function(nPosition, oMethod) { + var i; + nPosition = parseInt(nPosition, 10); + if (isNaN(nPosition)) { + return false; + } + for (i=0; i < onPositionItems.length; i++) { + if (nPosition === onPositionItems[i].position) { + if (!oMethod || (oMethod === onPositionItems[i].method)) { + if (onPositionItems[i].fired) { + onPositionFired--; + } + onPositionItems.splice(i, 1); + } + } + } + }; + this._processOnPosition = function() { + var i, item, j = onPositionItems.length; + if (!j || !s.playState || onPositionFired >= j) { + return false; + } + for (i=j-1; i >= 0; i--) { + item = onPositionItems[i]; + if (!item.fired && s.position >= item.position) { + item.fired = true; + onPositionFired++; + item.method.apply(item.scope, [item.position]); + } + } + return true; + }; + this._resetOnPosition = function(nPosition) { + var i, item, j = onPositionItems.length; + if (!j) { + return false; + } + for (i=j-1; i >= 0; i--) { + item = onPositionItems[i]; + if (item.fired && nPosition <= item.position) { + item.fired = false; + onPositionFired--; + } + } + return true; + }; + applyFromTo = function() { + var instanceOptions = s._iO, + f = instanceOptions.from, + t = instanceOptions.to, + start, end; + end = function() { + s.clearOnPosition(t, end); + s.stop(); + }; + start = function() { + if (t !== null && !isNaN(t)) { + s.onPosition(t, end); + } + }; + if (f !== null && !isNaN(f)) { + instanceOptions.position = f; + instanceOptions.multiShot = false; + start(); + } + return instanceOptions; + }; + attachOnPosition = function() { + var item, + op = s._iO.onposition; + if (op) { + for (item in op) { + if (op.hasOwnProperty(item)) { + s.onPosition(parseInt(item, 10), op[item]); + } + } + } + }; + detachOnPosition = function() { + var item, + op = s._iO.onposition; + if (op) { + for (item in op) { + if (op.hasOwnProperty(item)) { + s.clearOnPosition(parseInt(item, 10)); + } + } + } + }; + start_html5_timer = function() { + if (s.isHTML5) { + startTimer(s); + } + }; + stop_html5_timer = function() { + if (s.isHTML5) { + stopTimer(s); + } + }; + resetProperties = function(retainPosition) { + if (!retainPosition) { + onPositionItems = []; + onPositionFired = 0; + } + onplay_called = false; + s._hasTimer = null; + s._a = null; + s._html5_canplay = false; + s.bytesLoaded = null; + s.bytesTotal = null; + s.duration = (s._iO && s._iO.duration ? s._iO.duration : null); + s.durationEstimate = null; + s.buffered = []; + s.eqData = []; + s.eqData.left = []; + s.eqData.right = []; + s.failures = 0; + s.isBuffering = false; + s.instanceOptions = {}; + s.instanceCount = 0; + s.loaded = false; + s.metadata = {}; + s.readyState = 0; + s.muted = false; + s.paused = false; + s.peakData = { + left: 0, + right: 0 + }; + s.waveformData = { + left: [], + right: [] + }; + s.playState = 0; + s.position = null; + s.id3 = {}; + }; + resetProperties(); + this._onTimer = function(bForce) { + var duration, isNew = false, time, x = {}; + if (s._hasTimer || bForce) { + if (s._a && (bForce || ((s.playState > 0 || s.readyState === 1) && !s.paused))) { + duration = s._get_html5_duration(); + if (duration !== lastHTML5State.duration) { + lastHTML5State.duration = duration; + s.duration = duration; + isNew = true; + } + s.durationEstimate = s.duration; + time = (s._a.currentTime * msecScale || 0); + if (time !== lastHTML5State.time) { + lastHTML5State.time = time; + isNew = true; + } + if (isNew || bForce) { + s._whileplaying(time,x,x,x,x); + } + } + return isNew; + } + }; + this._get_html5_duration = function() { + var instanceOptions = s._iO, + d = (s._a && s._a.duration ? s._a.duration*msecScale : (instanceOptions && instanceOptions.duration ? instanceOptions.duration : null)), + result = (d && !isNaN(d) && d !== Infinity ? d : null); + return result; + }; + this._apply_loop = function(a, nLoops) { + a.loop = (nLoops > 1 ? 'loop' : ''); + }; + this._setup_html5 = function(oOptions) { + var instanceOptions = mixin(s._iO, oOptions), + a = useGlobalHTML5Audio ? globalHTML5Audio : s._a, + dURL = decodeURI(instanceOptions.url), + sameURL; + if (useGlobalHTML5Audio) { + if (dURL === decodeURI(lastGlobalHTML5URL)) { + sameURL = true; + } + } else if (dURL === decodeURI(lastURL)) { + sameURL = true; + } + if (a) { + if (a._s) { + if (useGlobalHTML5Audio) { + if (a._s && a._s.playState && !sameURL) { + a._s.stop(); + } + } else if (!useGlobalHTML5Audio && dURL === decodeURI(lastURL)) { + s._apply_loop(a, instanceOptions.loops); + return a; + } + } + if (!sameURL) { + resetProperties(false); + a.src = instanceOptions.url; + s.url = instanceOptions.url; + lastURL = instanceOptions.url; + lastGlobalHTML5URL = instanceOptions.url; + a._called_load = false; + } + } else { + if (instanceOptions.autoLoad || instanceOptions.autoPlay) { + s._a = new Audio(instanceOptions.url); + } else { + s._a = (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()); + } + a = s._a; + a._called_load = false; + if (useGlobalHTML5Audio) { + globalHTML5Audio = a; + } + } + s.isHTML5 = true; + s._a = a; + a._s = s; + add_html5_events(); + s._apply_loop(a, instanceOptions.loops); + if (instanceOptions.autoLoad || instanceOptions.autoPlay) { + s.load(); + } else { + a.autobuffer = false; + a.preload = 'auto'; + } + return a; + }; + add_html5_events = function() { + if (s._a._added_events) { + return false; + } + var f; + function add(oEvt, oFn, bCapture) { + return s._a ? s._a.addEventListener(oEvt, oFn, bCapture||false) : null; + } + s._a._added_events = true; + for (f in html5_events) { + if (html5_events.hasOwnProperty(f)) { + add(f, html5_events[f]); + } + } + return true; + }; + remove_html5_events = function() { + var f; + function remove(oEvt, oFn, bCapture) { + return (s._a ? s._a.removeEventListener(oEvt, oFn, bCapture||false) : null); + } + s._a._added_events = false; + for (f in html5_events) { + if (html5_events.hasOwnProperty(f)) { + remove(f, html5_events[f]); + } + } + }; + this._onload = function(nSuccess) { + var fN, + loadOK = !!nSuccess || (!s.isHTML5 && fV === 8 && s.duration); + s.loaded = loadOK; + s.readyState = loadOK?3:2; + s._onbufferchange(0); + if (s._iO.onload) { + wrapCallback(s, function() { + s._iO.onload.apply(s, [loadOK]); + }); + } + return true; + }; + this._onbufferchange = function(nIsBuffering) { + if (s.playState === 0) { + return false; + } + if ((nIsBuffering && s.isBuffering) || (!nIsBuffering && !s.isBuffering)) { + return false; + } + s.isBuffering = (nIsBuffering === 1); + if (s._iO.onbufferchange) { + s._iO.onbufferchange.apply(s); + } + return true; + }; + this._onsuspend = function() { + if (s._iO.onsuspend) { + s._iO.onsuspend.apply(s); + } + return true; + }; + this._onfailure = function(msg, level, code) { + s.failures++; + if (s._iO.onfailure && s.failures === 1) { + s._iO.onfailure(s, msg, level, code); + } else { + } + }; + this._onfinish = function() { + var io_onfinish = s._iO.onfinish; + s._onbufferchange(0); + s._resetOnPosition(0); + if (s.instanceCount) { + s.instanceCount--; + if (!s.instanceCount) { + detachOnPosition(); + s.playState = 0; + s.paused = false; + s.instanceCount = 0; + s.instanceOptions = {}; + s._iO = {}; + stop_html5_timer(); + if (s.isHTML5) { + s.position = 0; + } + } + if (!s.instanceCount || s._iO.multiShotEvents) { + if (io_onfinish) { + wrapCallback(s, function() { + io_onfinish.apply(s); + }); + } + } + } + }; + this._whileloading = function(nBytesLoaded, nBytesTotal, nDuration, nBufferLength) { + var instanceOptions = s._iO; + s.bytesLoaded = nBytesLoaded; + s.bytesTotal = nBytesTotal; + s.duration = Math.floor(nDuration); + s.bufferLength = nBufferLength; + if (!s.isHTML5 && !instanceOptions.isMovieStar) { + if (instanceOptions.duration) { + s.durationEstimate = (s.duration > instanceOptions.duration) ? s.duration : instanceOptions.duration; + } else { + s.durationEstimate = parseInt((s.bytesTotal / s.bytesLoaded) * s.duration, 10); + } + } else { + s.durationEstimate = s.duration; + } + if (!s.isHTML5) { + s.buffered = [{ + 'start': 0, + 'end': s.duration + }]; + } + if ((s.readyState !== 3 || s.isHTML5) && instanceOptions.whileloading) { + instanceOptions.whileloading.apply(s); + } + }; + this._whileplaying = function(nPosition, oPeakData, oWaveformDataLeft, oWaveformDataRight, oEQData) { + var instanceOptions = s._iO, + eqLeft; + if (isNaN(nPosition) || nPosition === null) { + return false; + } + s.position = Math.max(0, nPosition); + s._processOnPosition(); + if (!s.isHTML5 && fV > 8) { + if (instanceOptions.usePeakData && oPeakData !== _undefined && oPeakData) { + s.peakData = { + left: oPeakData.leftPeak, + right: oPeakData.rightPeak + }; + } + if (instanceOptions.useWaveformData && oWaveformDataLeft !== _undefined && oWaveformDataLeft) { + s.waveformData = { + left: oWaveformDataLeft.split(','), + right: oWaveformDataRight.split(',') + }; + } + if (instanceOptions.useEQData) { + if (oEQData !== _undefined && oEQData && oEQData.leftEQ) { + eqLeft = oEQData.leftEQ.split(','); + s.eqData = eqLeft; + s.eqData.left = eqLeft; + if (oEQData.rightEQ !== _undefined && oEQData.rightEQ) { + s.eqData.right = oEQData.rightEQ.split(','); + } + } + } + } + if (s.playState === 1) { + if (!s.isHTML5 && fV === 8 && !s.position && s.isBuffering) { + s._onbufferchange(0); + } + if (instanceOptions.whileplaying) { + instanceOptions.whileplaying.apply(s); + } + } + return true; + }; + this._oncaptiondata = function(oData) { + s.captiondata = oData; + if (s._iO.oncaptiondata) { + s._iO.oncaptiondata.apply(s, [oData]); + } + }; + this._onmetadata = function(oMDProps, oMDData) { + var oData = {}, i, j; + for (i = 0, j = oMDProps.length; i < j; i++) { + oData[oMDProps[i]] = oMDData[i]; + } + s.metadata = oData; + if (s._iO.onmetadata) { + s._iO.onmetadata.apply(s); + } + }; + this._onid3 = function(oID3Props, oID3Data) { + var oData = [], i, j; + for (i = 0, j = oID3Props.length; i < j; i++) { + oData[oID3Props[i]] = oID3Data[i]; + } + s.id3 = mixin(s.id3, oData); + if (s._iO.onid3) { + s._iO.onid3.apply(s); + } + }; + this._onconnect = function(bSuccess) { + bSuccess = (bSuccess === 1); + s.connected = bSuccess; + if (bSuccess) { + s.failures = 0; + if (idCheck(s.id)) { + if (s.getAutoPlay()) { + s.play(_undefined, s.getAutoPlay()); + } else if (s._iO.autoLoad) { + s.load(); + } + } + if (s._iO.onconnect) { + s._iO.onconnect.apply(s, [bSuccess]); + } + } + }; + this._ondataerror = function(sError) { + if (s.playState > 0) { + if (s._iO.ondataerror) { + s._iO.ondataerror.apply(s); + } + } + }; + }; + getDocument = function() { + return (doc.body || doc._docElement || doc.getElementsByTagName('div')[0]); + }; + id = function(sID) { + return doc.getElementById(sID); + }; + mixin = function(oMain, oAdd) { + var o1 = (oMain || {}), o2, o; + o2 = (oAdd === _undefined ? sm2.defaultOptions : oAdd); + for (o in o2) { + if (o2.hasOwnProperty(o) && o1[o] === _undefined) { + if (typeof o2[o] !== 'object' || o2[o] === null) { + o1[o] = o2[o]; + } else { + o1[o] = mixin(o1[o], o2[o]); + } + } + } + return o1; + }; + wrapCallback = function(oSound, callback) { + if (!oSound.isHTML5 && fV === 8) { + window.setTimeout(callback, 0); + } else { + callback(); + } + }; + extraOptions = { + 'onready': 1, + 'ontimeout': 1, + 'defaultOptions': 1, + 'flash9Options': 1, + 'movieStarOptions': 1 + }; + assign = function(o, oParent) { + var i, + result = true, + hasParent = (oParent !== _undefined), + setupOptions = sm2.setupOptions, + bonusOptions = extraOptions; + for (i in o) { + if (o.hasOwnProperty(i)) { + if (typeof o[i] !== 'object' || o[i] === null || o[i] instanceof Array || o[i] instanceof RegExp) { + if (hasParent && bonusOptions[oParent] !== _undefined) { + sm2[oParent][i] = o[i]; + } else if (setupOptions[i] !== _undefined) { + sm2.setupOptions[i] = o[i]; + sm2[i] = o[i]; + } else if (bonusOptions[i] === _undefined) { + result = false; + } else { + if (sm2[i] instanceof Function) { + sm2[i].apply(sm2, (o[i] instanceof Array? o[i] : [o[i]])); + } else { + sm2[i] = o[i]; + } + } + } else { + if (bonusOptions[i] === _undefined) { + result = false; + } else { + return assign(o[i], i); + } + } + } + } + return result; + }; + function preferFlashCheck(kind) { + return (sm2.preferFlash && hasFlash && !sm2.ignoreFlash && (sm2.flash[kind] !== _undefined && sm2.flash[kind])); + } + event = (function() { + var old = (window.attachEvent), + evt = { + add: (old?'attachEvent':'addEventListener'), + remove: (old?'detachEvent':'removeEventListener') + }; + function getArgs(oArgs) { + var args = slice.call(oArgs), + len = args.length; + if (old) { + args[1] = 'on' + args[1]; + if (len > 3) { + args.pop(); + } + } else if (len === 3) { + args.push(false); + } + return args; + } + function apply(args, sType) { + var element = args.shift(), + method = [evt[sType]]; + if (old) { + element[method](args[0], args[1]); + } else { + element[method].apply(element, args); + } + } + function add() { + apply(getArgs(arguments), 'add'); + } + function remove() { + apply(getArgs(arguments), 'remove'); + } + return { + 'add': add, + 'remove': remove + }; + }()); + function html5_event(oFn) { + return function(e) { + var s = this._s, + result; + if (!s || !s._a) { + result = null; + } else { + result = oFn.call(this, e); + } + return result; + }; + } + html5_events = { + abort: html5_event(function() { + }), + canplay: html5_event(function() { + var s = this._s, + position1K; + if (s._html5_canplay) { + return true; + } + s._html5_canplay = true; + s._onbufferchange(0); + position1K = (s._iO.position !== _undefined && !isNaN(s._iO.position)?s._iO.position/msecScale:null); + if (s.position && this.currentTime !== position1K) { + try { + this.currentTime = position1K; + } catch(ee) { + } + } + if (s._iO._oncanplay) { + s._iO._oncanplay(); + } + }), + canplaythrough: html5_event(function() { + var s = this._s; + if (!s.loaded) { + s._onbufferchange(0); + s._whileloading(s.bytesLoaded, s.bytesTotal, s._get_html5_duration()); + s._onload(true); + } + }), + ended: html5_event(function() { + var s = this._s; + s._onfinish(); + }), + error: html5_event(function() { + this._s._onload(false); + }), + loadeddata: html5_event(function() { + var s = this._s; + if (!s._loaded && !isSafari) { + s.duration = s._get_html5_duration(); + } + }), + loadedmetadata: html5_event(function() { + }), + loadstart: html5_event(function() { + this._s._onbufferchange(1); + }), + play: html5_event(function() { + this._s._onbufferchange(0); + }), + playing: html5_event(function() { + this._s._onbufferchange(0); + }), + progress: html5_event(function(e) { + var s = this._s, + i, j, str, buffered = 0, + isProgress = (e.type === 'progress'), + ranges = e.target.buffered, + loaded = (e.loaded||0), + total = (e.total||1); + s.buffered = []; + if (ranges && ranges.length) { + for (i=0, j=ranges.length; i= 0; i--) { + support['audio/'+aF[item].related[i]] = support[item]; + sm2.html5[aF[item].related[i]] = support[item]; + sm2.flash[aF[item].related[i]] = support[item]; + } + } + } + } + support.canPlayType = (a?cp:null); + sm2.html5 = mixin(sm2.html5, support); + sm2.html5.usingFlash = featureCheck(); + needsFlash = sm2.html5.usingFlash; + return true; + }; + strings = { + }; + str = function() { + }; + loopFix = function(sOpt) { + if (fV === 8 && sOpt.loops > 1 && sOpt.stream) { + sOpt.stream = false; + } + return sOpt; + }; + policyFix = function(sOpt, sPre) { + if (sOpt && !sOpt.usePolicyFile && (sOpt.onid3 || sOpt.usePeakData || sOpt.useWaveformData || sOpt.useEQData)) { + sOpt.usePolicyFile = true; + } + return sOpt; + }; + complain = function(sMsg) { + }; + doNothing = function() { + return false; + }; + disableObject = function(o) { + var oProp; + for (oProp in o) { + if (o.hasOwnProperty(oProp) && typeof o[oProp] === 'function') { + o[oProp] = doNothing; + } + } + oProp = null; + }; + failSafely = function(bNoDisable) { + if (bNoDisable === _undefined) { + bNoDisable = false; + } + if (disabled || bNoDisable) { + sm2.disable(bNoDisable); + } + }; + normalizeMovieURL = function(smURL) { + var urlParams = null, url; + if (smURL) { + if (smURL.match(/\.swf(\?.*)?$/i)) { + urlParams = smURL.substr(smURL.toLowerCase().lastIndexOf('.swf?') + 4); + if (urlParams) { + return smURL; + } + } else if (smURL.lastIndexOf('/') !== smURL.length - 1) { + smURL += '/'; + } + } + url = (smURL && smURL.lastIndexOf('/') !== - 1 ? smURL.substr(0, smURL.lastIndexOf('/') + 1) : './') + sm2.movieURL; + if (sm2.noSWFCache) { + url += ('?ts=' + new Date().getTime()); + } + return url; + }; + setVersionInfo = function() { + fV = parseInt(sm2.flashVersion, 10); + if (fV !== 8 && fV !== 9) { + sm2.flashVersion = fV = defaultFlashVersion; + } + var isDebug = (sm2.debugMode || sm2.debugFlash?'_debug.swf':'.swf'); + if (sm2.useHTML5Audio && !sm2.html5Only && sm2.audioFormats.mp4.required && fV < 9) { + sm2.flashVersion = fV = 9; + } + sm2.version = sm2.versionNumber + (sm2.html5Only?' (HTML5-only mode)':(fV === 9?' (AS3/Flash 9)':' (AS2/Flash 8)')); + if (fV > 8) { + sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.flash9Options); + sm2.features.buffering = true; + sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.movieStarOptions); + sm2.filePatterns.flash9 = new RegExp('\\.(mp3|' + netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + sm2.features.movieStar = true; + } else { + sm2.features.movieStar = false; + } + sm2.filePattern = sm2.filePatterns[(fV !== 8?'flash9':'flash8')]; + sm2.movieURL = (fV === 8?'soundmanager2.swf':'soundmanager2_flash9.swf').replace('.swf', isDebug); + sm2.features.peakData = sm2.features.waveformData = sm2.features.eqData = (fV > 8); + }; + setPolling = function(bPolling, bHighPerformance) { + if (!flash) { + return false; + } + flash._setPolling(bPolling, bHighPerformance); + }; + initDebug = function() { + }; + idCheck = this.getSoundById; + getSWFCSS = function() { + var css = []; + if (sm2.debugMode) { + css.push(swfCSS.sm2Debug); + } + if (sm2.debugFlash) { + css.push(swfCSS.flashDebug); + } + if (sm2.useHighPerformance) { + css.push(swfCSS.highPerf); + } + return css.join(' '); + }; + flashBlockHandler = function() { + var name = str('fbHandler'), + p = sm2.getMoviePercent(), + css = swfCSS, + error = {type:'FLASHBLOCK'}; + if (sm2.html5Only) { + return false; + } + if (!sm2.ok()) { + if (needsFlash) { + sm2.oMC.className = getSWFCSS() + ' ' + css.swfDefault + ' ' + (p === null?css.swfTimedout:css.swfError); + } + sm2.didFlashBlock = true; + processOnEvents({type:'ontimeout', ignoreInit:true, error:error}); + catchError(error); + } else { + if (sm2.oMC) { + sm2.oMC.className = [getSWFCSS(), css.swfDefault, css.swfLoaded + (sm2.didFlashBlock?' '+css.swfUnblocked:'')].join(' '); + } + } + }; + addOnEvent = function(sType, oMethod, oScope) { + if (on_queue[sType] === _undefined) { + on_queue[sType] = []; + } + on_queue[sType].push({ + 'method': oMethod, + 'scope': (oScope || null), + 'fired': false + }); + }; + processOnEvents = function(oOptions) { + if (!oOptions) { + oOptions = { + type: (sm2.ok() ? 'onready' : 'ontimeout') + }; + } + if (!didInit && oOptions && !oOptions.ignoreInit) { + return false; + } + if (oOptions.type === 'ontimeout' && (sm2.ok() || (disabled && !oOptions.ignoreInit))) { + return false; + } + var status = { + success: (oOptions && oOptions.ignoreInit?sm2.ok():!disabled) + }, + srcQueue = (oOptions && oOptions.type?on_queue[oOptions.type]||[]:[]), + queue = [], i, j, + args = [status], + canRetry = (needsFlash && !sm2.ok()); + if (oOptions.error) { + args[0].error = oOptions.error; + } + for (i = 0, j = srcQueue.length; i < j; i++) { + if (srcQueue[i].fired !== true) { + queue.push(srcQueue[i]); + } + } + if (queue.length) { + for (i = 0, j = queue.length; i < j; i++) { + if (queue[i].scope) { + queue[i].method.apply(queue[i].scope, args); + } else { + queue[i].method.apply(this, args); + } + if (!canRetry) { + queue[i].fired = true; + } + } + } + return true; + }; + initUserOnload = function() { + window.setTimeout(function() { + if (sm2.useFlashBlock) { + flashBlockHandler(); + } + processOnEvents(); + if (typeof sm2.onload === 'function') { + sm2.onload.apply(window); + } + if (sm2.waitForWindowLoad) { + event.add(window, 'load', initUserOnload); + } + },1); + }; + detectFlash = function() { + if (hasFlash !== _undefined) { + return hasFlash; + } + var hasPlugin = false, n = navigator, nP = n.plugins, obj, type, types, AX = window.ActiveXObject; + if (nP && nP.length) { + type = 'application/x-shockwave-flash'; + types = n.mimeTypes; + if (types && types[type] && types[type].enabledPlugin && types[type].enabledPlugin.description) { + hasPlugin = true; + } + } else if (AX !== _undefined && !ua.match(/MSAppHost/i)) { + try { + obj = new AX('ShockwaveFlash.ShockwaveFlash'); + } catch(e) { + obj = null; + } + hasPlugin = (!!obj); + obj = null; + } + hasFlash = hasPlugin; + return hasPlugin; + }; + featureCheck = function() { + var flashNeeded, + item, + formats = sm2.audioFormats, + isSpecial = (is_iDevice && !!(ua.match(/os (1|2|3_0|3_1)/i))); + if (isSpecial) { + sm2.hasHTML5 = false; + sm2.html5Only = true; + if (sm2.oMC) { + sm2.oMC.style.display = 'none'; + } + } else { + if (sm2.useHTML5Audio) { + if (!sm2.html5 || !sm2.html5.canPlayType) { + sm2.hasHTML5 = false; + } + } + } + if (sm2.useHTML5Audio && sm2.hasHTML5) { + canIgnoreFlash = true; + for (item in formats) { + if (formats.hasOwnProperty(item)) { + if (formats[item].required) { + if (!sm2.html5.canPlayType(formats[item].type)) { + canIgnoreFlash = false; + flashNeeded = true; + } else if (sm2.preferFlash && (sm2.flash[item] || sm2.flash[formats[item].type])) { + flashNeeded = true; + } + } + } + } + } + if (sm2.ignoreFlash) { + flashNeeded = false; + canIgnoreFlash = true; + } + sm2.html5Only = (sm2.hasHTML5 && sm2.useHTML5Audio && !flashNeeded); + return (!sm2.html5Only); + }; + parseURL = function(url) { + var i, j, urlResult = 0, result; + if (url instanceof Array) { + for (i=0, j=url.length; i= 0; i--) { + if (sm2.sounds[sm2.soundIDs[i]].isHTML5 && sm2.sounds[sm2.soundIDs[i]]._hasTimer) { + sm2.sounds[sm2.soundIDs[i]]._onTimer(); + } + } + }; + catchError = function(options) { + options = (options !== _undefined ? options : {}); + if (typeof sm2.onerror === 'function') { + sm2.onerror.apply(window, [{type:(options.type !== _undefined ? options.type : null)}]); + } + if (options.fatal !== _undefined && options.fatal) { + sm2.disable(); + } + }; + badSafariFix = function() { + if (!isBadSafari || !detectFlash()) { + return false; + } + var aF = sm2.audioFormats, i, item; + for (item in aF) { + if (aF.hasOwnProperty(item)) { + if (item === 'mp3' || item === 'mp4') { + sm2.html5[item] = false; + if (aF[item] && aF[item].related) { + for (i = aF[item].related.length-1; i >= 0; i--) { + sm2.html5[aF[item].related[i]] = false; + } + } + } + } + } + }; + this._setSandboxType = function(sandboxType) { + }; + this._externalInterfaceOK = function(swfVersion) { + if (sm2.swfLoaded) { + return false; + } + var e; + sm2.swfLoaded = true; + tryInitOnFocus = false; + if (isBadSafari) { + badSafariFix(); + } + setTimeout(init, isIE ? 100 : 1); + }; + createMovie = function(smID, smURL) { + if (didAppend && appendSuccess) { + return false; + } + function initMsg() { + } + if (sm2.html5Only) { + setVersionInfo(); + initMsg(); + sm2.oMC = id(sm2.movieID); + init(); + didAppend = true; + appendSuccess = true; + return false; + } + var remoteURL = (smURL || sm2.url), + localURL = (sm2.altURL || remoteURL), + swfTitle = 'JS/Flash audio component (SoundManager 2)', + oTarget = getDocument(), + extraClass = getSWFCSS(), + isRTL = null, + html = doc.getElementsByTagName('html')[0], + oEmbed, oMovie, tmp, movieHTML, oEl, s, x, sClass; + isRTL = (html && html.dir && html.dir.match(/rtl/i)); + smID = (smID === _undefined?sm2.id:smID); + function param(name, value) { + return ''; + } + setVersionInfo(); + sm2.url = normalizeMovieURL(overHTTP?remoteURL:localURL); + smURL = sm2.url; + sm2.wmode = (!sm2.wmode && sm2.useHighPerformance ? 'transparent' : sm2.wmode); + if (sm2.wmode !== null && (ua.match(/msie 8/i) || (!isIE && !sm2.useHighPerformance)) && navigator.platform.match(/win32|win64/i)) { + messages.push(strings.spcWmode); + sm2.wmode = null; + } + oEmbed = { + 'name': smID, + 'id': smID, + 'src': smURL, + 'quality': 'high', + 'allowScriptAccess': sm2.allowScriptAccess, + 'bgcolor': sm2.bgColor, + 'pluginspage': http+'www.macromedia.com/go/getflashplayer', + 'title': swfTitle, + 'type': 'application/x-shockwave-flash', + 'wmode': sm2.wmode, + 'hasPriority': 'true' + }; + if (sm2.debugFlash) { + oEmbed.FlashVars = 'debug=1'; + } + if (!sm2.wmode) { + delete oEmbed.wmode; + } + if (isIE) { + oMovie = doc.createElement('div'); + movieHTML = [ + '', + param('movie', smURL), + param('AllowScriptAccess', sm2.allowScriptAccess), + param('quality', oEmbed.quality), + (sm2.wmode? param('wmode', sm2.wmode): ''), + param('bgcolor', sm2.bgColor), + param('hasPriority', 'true'), + (sm2.debugFlash ? param('FlashVars', oEmbed.FlashVars) : ''), + '' + ].join(''); + } else { + oMovie = doc.createElement('embed'); + for (tmp in oEmbed) { + if (oEmbed.hasOwnProperty(tmp)) { + oMovie.setAttribute(tmp, oEmbed[tmp]); + } + } + } + initDebug(); + extraClass = getSWFCSS(); + oTarget = getDocument(); + if (oTarget) { + sm2.oMC = (id(sm2.movieID) || doc.createElement('div')); + if (!sm2.oMC.id) { + sm2.oMC.id = sm2.movieID; + sm2.oMC.className = swfCSS.swfDefault + ' ' + extraClass; + s = null; + oEl = null; + if (!sm2.useFlashBlock) { + if (sm2.useHighPerformance) { + s = { + 'position': 'fixed', + 'width': '8px', + 'height': '8px', + 'bottom': '0px', + 'left': '0px', + 'overflow': 'hidden' + }; + } else { + s = { + 'position': 'absolute', + 'width': '6px', + 'height': '6px', + 'top': '-9999px', + 'left': '-9999px' + }; + if (isRTL) { + s.left = Math.abs(parseInt(s.left,10))+'px'; + } + } + } + if (isWebkit) { + sm2.oMC.style.zIndex = 10000; + } + if (!sm2.debugFlash) { + for (x in s) { + if (s.hasOwnProperty(x)) { + sm2.oMC.style[x] = s[x]; + } + } + } + try { + if (!isIE) { + sm2.oMC.appendChild(oMovie); + } + oTarget.appendChild(sm2.oMC); + if (isIE) { + oEl = sm2.oMC.appendChild(doc.createElement('div')); + oEl.className = swfCSS.swfBox; + oEl.innerHTML = movieHTML; + } + appendSuccess = true; + } catch(e) { + throw new Error(str('domError')+' \n'+e.toString()); + } + } else { + sClass = sm2.oMC.className; + sm2.oMC.className = (sClass?sClass+' ':swfCSS.swfDefault) + (extraClass?' '+extraClass:''); + sm2.oMC.appendChild(oMovie); + if (isIE) { + oEl = sm2.oMC.appendChild(doc.createElement('div')); + oEl.className = swfCSS.swfBox; + oEl.innerHTML = movieHTML; + } + appendSuccess = true; + } + } + didAppend = true; + initMsg(); + return true; + }; + initMovie = function() { + if (sm2.html5Only) { + createMovie(); + return false; + } + if (flash) { + return false; + } + if (!sm2.url) { + return false; + } + flash = sm2.getMovie(sm2.id); + if (!flash) { + if (!oRemoved) { + createMovie(sm2.id, sm2.url); + } else { + if (!isIE) { + sm2.oMC.appendChild(oRemoved); + } else { + sm2.oMC.innerHTML = oRemovedHTML; + } + oRemoved = null; + didAppend = true; + } + flash = sm2.getMovie(sm2.id); + } + if (typeof sm2.oninitmovie === 'function') { + setTimeout(sm2.oninitmovie, 1); + } + return true; + }; + delayWaitForEI = function() { + setTimeout(waitForEI, 1000); + }; + waitForEI = function() { + var p, + loadIncomplete = false; + if (!sm2.url) { + return false; + } + if (waitingForEI) { + return false; + } + waitingForEI = true; + event.remove(window, 'load', delayWaitForEI); + if (tryInitOnFocus && !isFocused) { + return false; + } + if (!didInit) { + p = sm2.getMoviePercent(); + if (p > 0 && p < 100) { + loadIncomplete = true; + } + } + setTimeout(function() { + p = sm2.getMoviePercent(); + if (loadIncomplete) { + waitingForEI = false; + window.setTimeout(delayWaitForEI, 1); + return false; + } + if (!didInit && okToDisable) { + if (p === null) { + if (sm2.useFlashBlock || sm2.flashLoadTimeout === 0) { + if (sm2.useFlashBlock) { + flashBlockHandler(); + } + } else { + if (!sm2.useFlashBlock && canIgnoreFlash) { + window.setTimeout(function() { + sm2.setup({ + preferFlash: false + }).reboot(); + sm2.didFlashBlock = true; + sm2.beginDelayedInit(); + }, 1); + } else { + processOnEvents({type:'ontimeout', ignoreInit: true}); + } + } + } else { + if (sm2.flashLoadTimeout === 0) { + } else { + failSafely(true); + } + } + } + }, sm2.flashLoadTimeout); + }; + handleFocus = function() { + function cleanup() { + event.remove(window, 'focus', handleFocus); + } + if (isFocused || !tryInitOnFocus) { + cleanup(); + return true; + } + okToDisable = true; + isFocused = true; + waitingForEI = false; + delayWaitForEI(); + cleanup(); + return true; + }; + flushMessages = function() { + }; + showSupport = function() { + }; + initComplete = function(bNoDisable) { + if (didInit) { + return false; + } + if (sm2.html5Only) { + didInit = true; + initUserOnload(); + return true; + } + var wasTimeout = (sm2.useFlashBlock && sm2.flashLoadTimeout && !sm2.getMoviePercent()), + result = true, + error; + if (!wasTimeout) { + didInit = true; + if (disabled) { + error = {type: (!hasFlash && needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT')}; + } + } + if (disabled || bNoDisable) { + if (sm2.useFlashBlock && sm2.oMC) { + sm2.oMC.className = getSWFCSS() + ' ' + (sm2.getMoviePercent() === null?swfCSS.swfTimedout:swfCSS.swfError); + } + processOnEvents({type:'ontimeout', error:error, ignoreInit: true}); + catchError(error); + result = false; + } else { + } + if (!disabled) { + if (sm2.waitForWindowLoad && !windowLoaded) { + event.add(window, 'load', initUserOnload); + } else { + initUserOnload(); + } + } + return result; + }; + setProperties = function() { + var i, + o = sm2.setupOptions; + for (i in o) { + if (o.hasOwnProperty(i)) { + if (sm2[i] === _undefined) { + sm2[i] = o[i]; + } else if (sm2[i] !== o[i]) { + sm2.setupOptions[i] = sm2[i]; + } + } + } + }; + init = function() { + if (didInit) { + return false; + } + function cleanup() { + event.remove(window, 'load', sm2.beginDelayedInit); + } + if (sm2.html5Only) { + if (!didInit) { + cleanup(); + sm2.enabled = true; + initComplete(); + } + return true; + } + initMovie(); + try { + flash._externalInterfaceTest(false); + setPolling(true, (sm2.flashPollingInterval || (sm2.useHighPerformance ? 10 : 50))); + if (!sm2.debugMode) { + flash._disableDebug(); + } + sm2.enabled = true; + if (!sm2.html5Only) { + event.add(window, 'unload', doNothing); + } + } catch(e) { + catchError({type:'JS_TO_FLASH_EXCEPTION', fatal:true}); + failSafely(true); + initComplete(); + return false; + } + initComplete(); + cleanup(); + return true; + }; + domContentLoaded = function() { + if (didDCLoaded) { + return false; + } + didDCLoaded = true; + setProperties(); + initDebug(); + if (!hasFlash && sm2.hasHTML5) { + sm2.setup({ + 'useHTML5Audio': true, + 'preferFlash': false + }); + } + testHTML5(); + if (!hasFlash && needsFlash) { + messages.push(strings.needFlash); + sm2.setup({ + 'flashLoadTimeout': 1 + }); + } + if (doc.removeEventListener) { + doc.removeEventListener('DOMContentLoaded', domContentLoaded, false); + } + initMovie(); + return true; + }; + domContentLoadedIE = function() { + if (doc.readyState === 'complete') { + domContentLoaded(); + doc.detachEvent('onreadystatechange', domContentLoadedIE); + } + return true; + }; + winOnLoad = function() { + windowLoaded = true; + event.remove(window, 'load', winOnLoad); + }; + preInit = function() { + if (mobileHTML5) { + sm2.setupOptions.useHTML5Audio = true; + sm2.setupOptions.preferFlash = false; + if (is_iDevice || (isAndroid && !ua.match(/android\s2\.3/i))) { + if (is_iDevice) { + sm2.ignoreFlash = true; + } + useGlobalHTML5Audio = true; + } + } + }; + preInit(); + detectFlash(); + event.add(window, 'focus', handleFocus); + event.add(window, 'load', delayWaitForEI); + event.add(window, 'load', winOnLoad); + if (doc.addEventListener) { + doc.addEventListener('DOMContentLoaded', domContentLoaded, false); + } else if (doc.attachEvent) { + doc.attachEvent('onreadystatechange', domContentLoadedIE); + } else { + catchError({type:'NO_DOM2_EVENTS', fatal:true}); + } +} +// SM2_DEFER details: http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading +if (window.SM2_DEFER === undefined || !SM2_DEFER) { + soundManager = new SoundManager(); +} +window.SoundManager = SoundManager; +window.soundManager = soundManager; +}(window)); \ No newline at end of file diff --git a/public/scripts/shared/init.coffee b/public/scripts/shared/init.coffee new file mode 100644 index 00000000..db33acf1 --- /dev/null +++ b/public/scripts/shared/init.coffee @@ -0,0 +1,9 @@ +def = new $.Deferred() + +pfm.soundManager = def.promise() + +soundManager.setup + url: '/flash/soundmanager/' + flashVersion: 9 + onready: () -> + def.resolve() diff --git a/public/styles/albums.less b/public/styles/albums.less index 8e1bc836..de9f49f8 100644 --- a/public/styles/albums.less +++ b/public/styles/albums.less @@ -18,9 +18,11 @@ &.empty { .alert(); + .border-radius(0px); float: none !important; width: auto !important; display: block; + margin-top: 5px; padding: 5px; font-size: 9pt; } diff --git a/public/styles/components.less b/public/styles/components.less index a10a21ff..e4f3aaa1 100644 --- a/public/styles/components.less +++ b/public/styles/components.less @@ -200,7 +200,7 @@ html { .pagination { border: none; - background: #ccc; + background: #ddd; margin: 5px 0px; padding: 0px; overflow: hidden; diff --git a/public/styles/layout.less b/public/styles/layout.less index 63a91593..7470ecb3 100644 --- a/public/styles/layout.less +++ b/public/styles/layout.less @@ -13,7 +13,6 @@ html body { .background-color { background: rgba(42, 42, 42, 1); - background-image: url('/images/pattern4.jpg'); position: fixed; left: 0px; top: 0px; @@ -97,27 +96,46 @@ header { .image { float: left; - width: 35px; - height: 35px; + width: 40px; + height: 40px; background: #aaa; + + img { + display: block; + width: 100%; + height: 100%; + } } .transport { - display: none; - margin-top: 5px; - margin-left: 6px; + margin-top: -3px; + margin-bottom: 3px; + margin-left: 46px; height: 5px; background: #555; position: relative; margin-right: 6px; + cursor: pointer; - .bar { - background: #fd8500; + &:hover { + } + + .bar, .loader-bar { position: absolute; height: 100%; top: 0px; left: 0px; } + + .loader-bar { + background: #888; + z-index: 200; + } + + .bar { + background: #fd8500; + z-index: 500; + } } .title { @@ -149,13 +167,14 @@ header { margin: 0px; padding: 0px; list-style: none; - margin-top: 6px; - margin-right: 6px; + margin-top: 3px; + margin-right: 5px; li { float: left; margin: 0px; padding: 0px; + position: relative; a { display: block; @@ -165,9 +184,52 @@ header { color: #ddd; } - &:hover { + .volume-slider { + .box-shadow(0px 5px 9px rgba(50, 50, 50, 0.5)); + + position: absolute; + border-bottom: 1px solid #fff; + border-top: 3px solid #3366CC; + top: 30px; + background: #eee; + list-style: none; + margin: 0px; + display: none; + width: 100%; + padding: 10px 0px; + z-index: 300; + + .bar { + width: 5px; + height: 180px; + background: #aaa; + margin: auto; + position: relative; + cursor: pointer; + + .knob { + .border-radius(20px); + height: 15px; + width: 15px; + display: block; + background: #1F3D7B; + border: 2px solid #1F3D7B; + position: absolute; + top: 0px; + left: -7px; + font-size: 1px; + padding: 0px; + } + } + } + + &:hover, &.keep-open { #gradient>.vertical(#fff, #ddd); + .volume-slider { + display: block; + } + a { text-decoration: none; color: #000; diff --git a/public/styles/tracks.less b/public/styles/tracks.less index f8adfe64..cde76aa0 100644 --- a/public/styles/tracks.less +++ b/public/styles/tracks.less @@ -1,8 +1,44 @@ @import-once 'base/bootstrap/bootstrap'; @import-once 'mixins'; +.comments { + ul { + margin: 0px; + padding: 0px; + list-style: none; + margin-bottom: 5px; + + li { + .clearfix(); + margin: 0px; + padding: 3px; + border-bottom: 1px solid #ddd; + + .meta { + font-size: 8pt; + float: right; + } + + .content { + margin-left: 45px; + + } + + img { + .img-polaroid(); + + float: left; + width: 32px; + height: 32px; + padding: 2px; + } + } + } +} + .dashboard { section { + /* .box-sizing(border-box); width: 50%; @@ -15,6 +51,7 @@ &:last-child { padding-left: 10px; } + */ } } @@ -60,8 +97,24 @@ overflow: hidden; margin: 10px 0px; padding: 0px; + line-height: normal; - &:hover { + &.empty { + .alert(); + .border-radius(0px); + float: none !important; + width: auto !important; + display: block; + margin-top: 5px; + padding: 5px; + font-size: 9pt; + + &:hover { + background-color: @warningBackground; + } + } + + &:hover, &.is-playing { background: #eee; .image { @@ -71,6 +124,22 @@ } } + &.is-favourited { + .icon-favourite { + color: @orange; + text-decoration: none; + + i { + color: #FFD76E; + text-shadow: 0px 0px 2px rgba(0,0,0,0.8); + + &:before { + content: "\f005" + } + } + } + } + .image { height: 40px; width: 40px; @@ -109,13 +178,18 @@ .icons { float: right; - font-size: 13px; + font-size: 14px; margin-right: 2px; + a { + text-decoration: none; + } + a, span { display: block; float: left; margin: 0px 3px; + line-height: 0px; } .icon-microphone-off { @@ -166,7 +240,7 @@ } } -.user-details, .track-details, .album-details { +.user-details, .track-details, .album-details, .playlist-details { .comments { .alert { .border-radius(0px); @@ -212,11 +286,8 @@ } } -.track-details, .album-details { +.track-details, .album-details, .playlist-details { h1 { - .box-shadow(0px 2px 3px rgba(0, 0, 0, .3)); - background: #eee; - padding: 5px; } .lyrics { @@ -263,6 +334,45 @@ } } +.track-details { + h1 { + margin-left: 55px; + } + + .tracks-listing li .image .play-button { + display: block; + } + + .tracks-listing { + float: left; + margin-top: 5px; + } + + .tracks-listing li { + margin: 0px; + margin-bottom: 5px; + float: left; + + .image { + height: 50px; + width: 50px; + + .play-button { + line-height: 48px; + } + + img { + width: 48px; + height: 48px; + } + } + + .icons, .info { + display: none; + } + } +} + html { .track-toolbar { padding: 5px; diff --git a/public/templates/albums/show.html b/public/templates/albums/show.html index e2f8171c..958f75ed 100644 --- a/public/templates/albums/show.html +++ b/public/templates/albums/show.html @@ -5,10 +5,7 @@

    Recent Tracks

    diff --git a/public/templates/auth/login.html b/public/templates/auth/login.html index a7c6fff0..e7d40ead 100644 --- a/public/templates/auth/login.html +++ b/public/templates/auth/login.html @@ -1,4 +1,7 @@
    -

    Login!

    +

    Login

    +
    + Only user accounts that were created as of the launch of the pre-release will be available. +
    \ No newline at end of file diff --git a/public/templates/dashboard/index.html b/public/templates/dashboard/index.html index 13ef8de7..79356b2a 100644 --- a/public/templates/dashboard/index.html +++ b/public/templates/dashboard/index.html @@ -1,20 +1,11 @@
    -
    +

    see more The Newest Tunes

    - -
    -
    -
    \ No newline at end of file diff --git a/public/templates/directives/comments.html b/public/templates/directives/comments.html new file mode 100644 index 00000000..05a2f102 --- /dev/null +++ b/public/templates/directives/comments.html @@ -0,0 +1,21 @@ +
    +
    + There are no comments yet! +
    +
      +
    • +
      + {{comment.user.name}} posted + {{comment.created_at.date | momentFromNow}} +
      + +
      {{comment.content}}
      +
    • +
    +
    +
    + +
    + +
    +
    \ No newline at end of file diff --git a/public/templates/directives/favourite-button.html b/public/templates/directives/favourite-button.html new file mode 100644 index 00000000..1febe413 --- /dev/null +++ b/public/templates/directives/favourite-button.html @@ -0,0 +1,10 @@ + + + Favourite This! + + + + In Your Favourites + + + \ No newline at end of file diff --git a/public/templates/directives/player.html b/public/templates/directives/player.html new file mode 100644 index 00000000..fcd7f09b --- /dev/null +++ b/public/templates/directives/player.html @@ -0,0 +1,31 @@ +
    +
    + +
    +
    +
    +
    +
    + + +
    \ No newline at end of file diff --git a/public/templates/directives/tracks-list.html b/public/templates/directives/tracks-list.html index edeb9cc6..2bee314c 100644 --- a/public/templates/directives/tracks-list.html +++ b/public/templates/directives/tracks-list.html @@ -1,12 +1,15 @@ \ No newline at end of file diff --git a/public/templates/home/index.html b/public/templates/home/index.html index 539666b6..b289c709 100644 --- a/public/templates/home/index.html +++ b/public/templates/home/index.html @@ -1,13 +1,37 @@
    -

    Look, it's a website!

    +

    Pony.fm BETA 2 Preview

    +

    - This page should basically be a big advert for PFM. But that comes later. For now, you may do two things: + Welcome to Pony.fm's BETA 2 preview! If you were looking for the functional website, head on over to Pony.fm.

    -
    -
    -

    Login

    - -
    -
    +

    + This is an early look at the next Pony.fm release. Pony.fm is a music hosting website + for Bronies - and you can learn more about it here. +

    + +

    + This web application is under heavy development. While you are free to browse around, please note + that although the track and user data on this site is a copy of Pony.fm - all data will be erased when + the preview ends, or whenever we feel like. As such, please do not use this application for hosting your new music. +

    + +

    + At this time, many features are notably missing - such as: +

    +
      +
    • Mobile friendly design
    • +
    • Artist following
    • +
    • Notifications
    • +
    • Public playlists
    • +
    • Music Downloads
    • +
    +

    + And more. The layout and design is also subject to change in the future. +

    +

    + However, we welcome any and all feedback - so if you have a suggestion or bug report, you can hop on over to + Pony.fm's Forum. +

    +

    We hope you enjoy taking a look at the future of Pony.fm as much as we did building it!

    \ No newline at end of file diff --git a/public/templates/pages/about.html b/public/templates/pages/about.html index fbe6d73e..e2ee6d39 100644 --- a/public/templates/pages/about.html +++ b/public/templates/pages/about.html @@ -1,11 +1,37 @@
    -

    About!

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda id, illum in incidunt nemo nisi perspiciatis - quibusdam quos repellat veniam? Accusamus animi ducimus id iure repellendus tempore veniam voluptates - voluptatibus!

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut beatae commodi corporis doloremque dolorum ex facilis - impedit perferendis praesentium quasi quis quisquam repellat sint temporibus, totam. Excepturi magni tempora - voluptates.

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda atque corporis debitis doloremque est expedita - illo ipsam itaque maiores modi nam placeat qui sit suscipit, totam unde vel veniam. Temporibus?

    +

    What exactly is Pony.fm, anyway?

    +
    +
    +
    +

    Some My Little Pony: Friendship is Magic fans - typically referred to as "bronies" are the musical type, and show their appreciation for the show by pouring their talent into fan music. +

    The brony fan music community is diverse, spanning genres from symphonic metal to trance and everything in between. But most importantly, the community creates music.

    +

    A lot of music.

    +

    All this music has to go somewhere. YouTube, SoundCloud, and Bandcamp are popular outlets that many brony musicians use to host their tunes. But no mainstream sites are specifically designed for our fandom's needs, and they're not particularly helpful if, as a listener, you're looking for pony fan music.

    +

    That's where Pony.fm comes in. Pony.fm is a community, hosting service, and music database rolled into one, with a generous dash of pony on top.

    +
    +
    + +
    +
    +

    So it's SoundCloud with ponies?

    +

    Eenope!

    +

    Pony.fm is an original project. Although it takes inspiration from a number of well-known services for the general public, Pony.fm is not specifically modeled after any one of them. As a fan site itself, Pony.fm is an experience all its own.

    +

    Simply put, "Pony.fm is Pony.fm."

    + +

    What makes Pony.fm special?

    +

    Pony.fm is a service created by a brony, for bronies. Every inch of the Pony.fm experience is crafted with ponies and bronies in mind. Some of the features necessarily resemble what you may find on other sites - lossless uploads, for example - but some features are specific to the pony fan music community.

    +

    Created as a service for the fandom, Pony.fm aims to be the one-stop shop for all things pony music, for artists and listeners alike.

    +
    +
    + +
    +
    +

    What does MLP Forums have to do with Pony.fm?

    +

    MLP Forums and Pony.fm share an owner, and each encompasses a different segment of the global My Little Pony: Friendship is Magic community. Put together, both sites are able to offer a richer "supercommunity" experience than either site could offer on its own.

    + +

    Who is behind Pony.fm?

    +

    Pony.fm is built and owned by Feld0, a pony-loving teenager who heard the call of code. Recognizing the need for a true pony-specific music hosting site, and realizing that MLP Forums provided him with the resources he needed to make it happen, he created a blank text file and started pumping code into his computer.

    +
    +
    +
    \ No newline at end of file diff --git a/public/templates/pages/faq.html b/public/templates/pages/faq.html index 8d9040c1..4696b413 100644 --- a/public/templates/pages/faq.html +++ b/public/templates/pages/faq.html @@ -1,23 +1,77 @@
    -

    FAQ!

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab asperiores autem beatae blanditiis earum enim illo nisi - nulla numquam, odio saepe sed sunt tenetur! Ab aperiam enim error harum soluta!

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid animi blanditiis corporis debitis dolore dolorum - expedita facere facilis fuga ipsa labore libero non odit praesentium quidem quo, repudiandae veniam voluptatem!

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Corporis doloribus hic ipsam, laudantium minima - necessitatibus, officia perferendis provident ratione sunt unde vitae. Consectetur, ducimus harum maiores molestias - possimus ratione vero!

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab at beatae, blanditiis culpa dicta dolor ducimus ex harum - ipsam iure laboriosam, mollitia nesciunt obcaecati pariatur quibusdam quod recusandae, sapiente veritatis!

    +

    Pony.fm FAQ

    +
    + -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis dignissimos esse nihil non odio possimus - ratione sapiente ullam! Assumenda corporis doloremque et ipsum laudantium nihil quae quidem sapiente temporibus, - veritatis!

    + +

    Why doesn't Pony.fm support MP3 files?

    +

    MP3 encoding is "lossy." Lossy means that, during the encoding process, quality gets sacrificed for a decrease in size.

    +

    Pony.fm does not provide only MP3's; it also provides OGG's and lossless FLAC's. Uploading a lossless file puts a "perfect" copy of your track in Pony.fm's file store, which can be offered up for download on its own for audiophiles who like CD or better-than-CD sound quality, but starting from a lossless original also allows Pony.fm to transcode a song to other lossy formats with only one degree of loss.

    -

    Adipisci aperiam cupiditate ea error et eum exercitationem facilis, hic minus molestiae nulla, omnis possimus - repudiandae temporibus voluptatem. Ab accusamus aspernatur assumenda dicta dolorum eos, inventore nam quo - recusandae tenetur.

    +

    Pony.fm accepts a lossless upload, which is converted to FLAC (if it isn't already FLAC) for storage. This leaves a "perfect," unblemished copy of the track in Pony.fm's file store.

    +

    An MP3 file can be created from the FLAC. Minimal quality is lost because creating an MP3 from the FLAC is as good as creating an MP3 directly from your DAW.

    +

    An OGG Vorbis file can be created from the FLAC. Minimal quality is lost because Pony.fm has a lossless copy of the track on file; thus, we don't have the "recompressing a compressed MP3" issue that is present if Pony.fm's "master file" was an MP3.

    -

    Accusamus ad alias aliquam architecto blanditiis dolor eos, est eveniet, impedit maxime nam officia quaerat - quibusdam, ratione sed. Aut eum ex illum ipsum, minus nobis nulla quibusdam quidem repellat unde?

    + +

    Why isn't my file being accepted for Upload?

    +

    Pony.fm analyzes all uploads to determine their format and check it against a whitelist; the file extension is ignored. Unfortunately, slight variations in AIFF and WAV files exist that need to be individually whitelisted.

    +

    The alpha should have nailed most of these by now, but if there are some that still are not being accepted, contact an admin with the specific file details and they will try to add them to the White List.

    + + +

    How do I Upload a song?

    +

    At the top right of your screen there should be a button titled "Upload" next to the "send feedback" one. Select the Upload button and a drop down menu will appear, select the first option titled "Track Uploader". You should now be on a screen displaying the uploader. Select the Green button titled "Add Files" and select your song from your computer. The track should now start its download.

    +

    Please be aware that Pony.fm doesn't support MP3 uploads.

    + + +

    How do you set an avatar?

    +

    Avatars in Pony.fm use a free service called Gravatar. To learn more about it, and setup your own Gravatar account, click here!

    + + +

    Why the connection to MLP Forums?

    +

    MLP Forums is one of the web's largest and most well known My Little Pony: Friendship is Magic forum communities. Formally linking the two sites together paves the way for a rich, cross-site community experience. Members of one site can easily jump into the other without the hassle of managing yet another account, and content can seamlessly be brought from MLP Forums to Pony.fm and vice versa in meaningful ways.

    + + +

    How do I send Feedback to the Developers?

    +

    At the top of your page should be a nifty little button to the left of the upload button that says "Send Feedback".

    +

    Click this and a form will pop up, just follow the two simple instructions and enter the information needed and click "submit".

    +

    All feedback is greatly appreciated on Pony FM and we do our hardest to keep this site functional and to keep all of you happy.

    + + +

    What is the "Poniverse" and what does Pony FM have to do with it?

    +

    The Poniverse is a network that links together several Brony sites ,such as MLP Forums, together to form a super community of sorts that provides sites that satisfy everyone's needs.

    +

    Pony FM is just one of those sites and sets out to provide Brony Musicians with their own special corner to share their work with others and to receive feedback from other musicians, and in lots of cases to form collaborations that can end up in great partnerships.

    + + +

    Can I view any site statistics?

    +

    You sure can!

    +

    At the bottom left of your screen there is a small button that says Site Stats that's nestled just below a button for Pony.fm Forum.

    +

    Click on the "Site Stats" button and you will be taken to a screen that shows you graphs depicting the number of Track Views, Track Downloads, Track Plays and User Registrations.

    + + +

    How do I get in contact with other Musicians on Pony.fm?

    +

    On each user's screen there is a nifty little section where you can leave comments. This is used best for providing feedback and to show them your support, but if you plan on trying to start a collaboration and would prefer a more private means of communication, underneath the user Bio, there is a "send a message" which will utilise the "Personal Messenger" from MLP Forums to allow you to send a message to that artist.

    + + +

    How do I report someone?

    +

    At the current time a report feature isn't quite installed into the site, however, email feld0@pony.fm and he would be glad to handle any moderating issues that you have. But to reiterate what was said before, there IS a report function in the works.

    + + +

    How do I download an artist' song?

    +

    Click on the song that you are looking to download and you will notice to the right of the screen is a button titled "Downloads".

    +

    Click this button and you will be brought a drop down menu with FLAC, MP3, OGG, AAC and ALAC file types for you to download.

    +

    Select your preferred file type to start the download and it should all be smooth sailing from there.

    +
    +
    \ No newline at end of file diff --git a/public/templates/playlists/show.html b/public/templates/playlists/show.html index e07708ce..7e948f67 100644 --- a/public/templates/playlists/show.html +++ b/public/templates/playlists/show.html @@ -1,3 +1,44 @@ -
    -

    {{playlist.title}}

    +
    + + + + +

    + {{playlist.title}} +

    + +
    +
    +
    +
    +

    +
    + +

    Tracks

    + + +

    Comments

    + +
    +
    +
    + + + + Share or Embed +
    +
    +
    \ No newline at end of file diff --git a/public/templates/tracks/_layout.html b/public/templates/tracks/_layout.html index d996ba45..7d15e7d4 100644 --- a/public/templates/tracks/_layout.html +++ b/public/templates/tracks/_layout.html @@ -1,8 +1,10 @@
    diff --git a/public/templates/tracks/search-list.html b/public/templates/tracks/search-list.html index 5579ac41..c5ec2ec5 100644 --- a/public/templates/tracks/search-list.html +++ b/public/templates/tracks/search-list.html @@ -1,3 +1,3 @@
    - +
    \ No newline at end of file diff --git a/public/templates/tracks/search.html b/public/templates/tracks/search.html index 20323387..fc23f333 100644 --- a/public/templates/tracks/search.html +++ b/public/templates/tracks/search.html @@ -1,6 +1,6 @@
    +

    {{track.title}} @@ -58,17 +61,7 @@

    Comments

    -
    -
    - There are no comments yet! -
    -
    -
    - -
    - -
    -
    +
    diff --git a/vendor/autoload.php b/vendor/autoload.php index 4a45da29..bc13abb0 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91::getLoader(); +return ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842::getLoader(); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 44ae0db8..5c884131 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -10,8 +10,12 @@ return array( 'AccountController' => $baseDir . '/app/controllers/AccountController.php', 'AlbumsController' => $baseDir . '/app/controllers/AlbumsController.php', 'ApiControllerBase' => $baseDir . '/app/controllers/ApiControllerBase.php', + 'Api\\Web\\AccountController' => $baseDir . '/app/controllers/Api/Web/AccountController.php', 'Api\\Web\\AlbumsController' => $baseDir . '/app/controllers/Api/Web/AlbumsController.php', + 'Api\\Web\\ArtistsController' => $baseDir . '/app/controllers/Api/Web/ArtistsController.php', 'Api\\Web\\AuthController' => $baseDir . '/app/controllers/Api/Web/AuthController.php', + 'Api\\Web\\DashboardController' => $baseDir . '/app/controllers/Api/Web/DashboardController.php', + 'Api\\Web\\FavouritesController' => $baseDir . '/app/controllers/Api/Web/FavouritesController.php', 'Api\\Web\\ImagesController' => $baseDir . '/app/controllers/Api/Web/ImagesController.php', 'Api\\Web\\PlaylistsController' => $baseDir . '/app/controllers/Api/Web/PlaylistsController.php', 'Api\\Web\\TaxonomiesController' => $baseDir . '/app/controllers/Api/Web/TaxonomiesController.php', @@ -126,16 +130,24 @@ return array( 'ClassPreloader\\Parser\\DirVisitor' => $vendorDir . '/classpreloader/classpreloader/src/ClassPreloader/Parser/DirVisitor.php', 'ClassPreloader\\Parser\\FileVisitor' => $vendorDir . '/classpreloader/classpreloader/src/ClassPreloader/Parser/FileVisitor.php', 'ClassPreloader\\Parser\\NodeTraverser' => $vendorDir . '/classpreloader/classpreloader/src/ClassPreloader/Parser/NodeTraverser.php', + 'Commands\\AddTrackToPlaylistCommand' => $baseDir . '/app/models/Commands/AddTrackToPlaylistCommand.php', 'Commands\\CommandBase' => $baseDir . '/app/models/Commands/CommandBase.php', 'Commands\\CommandResponse' => $baseDir . '/app/models/Commands/CommandResponse.php', 'Commands\\CreateAlbumCommand' => $baseDir . '/app/models/Commands/CreateAlbumCommand.php', + 'Commands\\CreatePlaylistCommand' => $baseDir . '/app/models/Commands/CreatePlaylistCommand.php', 'Commands\\DeleteAlbumCommand' => $baseDir . '/app/models/Commands/DeleteAlbumCommand.php', + 'Commands\\DeletePlaylistCommand' => $baseDir . '/app/models/Commands/DeletePlaylistCommand.php', 'Commands\\DeleteTrackCommand' => $baseDir . '/app/models/Commands/DeleteTrackCommand.php', 'Commands\\EditAlbumCommand' => $baseDir . '/app/models/Commands/EditAlbumCommand.php', + 'Commands\\EditPlaylistCommand' => $baseDir . '/app/models/Commands/EditPlaylistCommand.php', 'Commands\\EditTrackCommand' => $baseDir . '/app/models/Commands/EditTrackCommand.php', + 'Commands\\SaveAccountSettingsCommand' => $baseDir . '/app/models/Commands/SaveAccountSettingsCommand.php', + 'Commands\\ToggleFavouriteCommand' => $baseDir . '/app/models/Commands/ToggleFavouriteCommand.php', 'Commands\\UploadTrackCommand' => $baseDir . '/app/models/Commands/UploadTrackCommand.php', 'ContentController' => $baseDir . '/app/controllers/ContentController.php', 'CreateAlbums' => $baseDir . '/app/database/migrations/2013_07_28_060804_create_albums.php', + 'CreateComments' => $baseDir . '/app/database/migrations/2013_08_01_051337_create_comments.php', + 'CreateFavourites' => $baseDir . '/app/database/migrations/2013_08_01_024827_create_favourites.php', 'CreateImagesTable' => $baseDir . '/app/database/migrations/2013_07_26_230827_create_images_table.php', 'CreatePlaylists' => $baseDir . '/app/database/migrations/2013_07_28_135136_create_playlists.php', 'CreateSongsTable' => $baseDir . '/app/database/migrations/2013_07_28_034328_create_songs_table.php', @@ -408,9 +420,12 @@ return array( 'Doctrine\\DBAL\\Types\\VarDateTimeType' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php', 'Doctrine\\DBAL\\Version' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Version.php', 'Entities\\Album' => $baseDir . '/app/models/Entities/Album.php', + 'Entities\\Favourite' => $baseDir . '/app/models/Entities/Favourite.php', 'Entities\\Genre' => $baseDir . '/app/models/Entities/Genre.php', 'Entities\\Image' => $baseDir . '/app/models/Entities/Image.php', 'Entities\\License' => $baseDir . '/app/models/Entities/License.php', + 'Entities\\PinnedPlaylist' => $baseDir . '/app/models/Entities/PinnedPlaylist.php', + 'Entities\\Playlist' => $baseDir . '/app/models/Entities/Playlist.php', 'Entities\\ShowSong' => $baseDir . '/app/models/Entities/ShowSong.php', 'Entities\\Track' => $baseDir . '/app/models/Entities/Track.php', 'Entities\\TrackType' => $baseDir . '/app/models/Entities/TrackType.php', @@ -1834,6 +1849,7 @@ return array( 'Symfony\\Component\\Translation\\Writer\\TranslationWriter' => $vendorDir . '/symfony/translation/Symfony/Component/Translation/Writer/TranslationWriter.php', 'TestCase' => $baseDir . '/app/tests/TestCase.php', 'TracksController' => $baseDir . '/app/controllers/TracksController.php', + 'Traits\\SlugTrait' => $baseDir . '/app/models/Traits/SlugTrait.php', 'UsersController' => $baseDir . '/app/controllers/UsersController.php', 'Whoops\\Exception\\ErrorException' => $vendorDir . '/filp/whoops/src/Whoops/Exception/ErrorException.php', 'Whoops\\Exception\\Frame' => $vendorDir . '/filp/whoops/src/Whoops/Exception/Frame.php', diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index da704e3b..2514921f 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php generated by Composer -class ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91 +class ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842 { private static $loader; @@ -19,9 +19,9 @@ class ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842', 'loadClassLoader')); $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir);