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 @@
@@ -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 00000000..dc383ce7
Binary files /dev/null and b/public/flash/soundmanager/soundmanager2.swf differ
diff --git a/public/flash/soundmanager/soundmanager2_debug.swf b/public/flash/soundmanager/soundmanager2_debug.swf
new file mode 100644
index 00000000..bfa601ae
Binary files /dev/null and b/public/flash/soundmanager/soundmanager2_debug.swf differ
diff --git a/public/flash/soundmanager/soundmanager2_flash9.swf b/public/flash/soundmanager/soundmanager2_flash9.swf
new file mode 100644
index 00000000..df23168d
Binary files /dev/null and b/public/flash/soundmanager/soundmanager2_flash9.swf differ
diff --git a/public/flash/soundmanager/soundmanager2_flash9_debug.swf b/public/flash/soundmanager/soundmanager2_flash9_debug.swf
new file mode 100644
index 00000000..adc545a4
Binary files /dev/null and b/public/flash/soundmanager/soundmanager2_flash9_debug.swf differ
diff --git a/public/flash/soundmanager/soundmanager2_flash_xdomain.zip b/public/flash/soundmanager/soundmanager2_flash_xdomain.zip
new file mode 100644
index 00000000..059fc115
Binary files /dev/null and b/public/flash/soundmanager/soundmanager2_flash_xdomain.zip differ
diff --git a/public/scripts/app/controllers/application.coffee b/public/scripts/app/controllers/application.coffee
index e3f5a43f..4f62802b 100644
--- a/public/scripts/app/controllers/application.coffee
+++ b/public/scripts/app/controllers/application.coffee
@@ -1,11 +1,19 @@
angular.module('ponyfm').controller "application", [
- '$scope', 'auth', '$location', 'upload', '$state', '$stateParams', '$injector'
- ($scope, auth, $location, upload, $state, $stateParams, $injector) ->
+ '$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 @@
diff --git a/public/templates/artists/_show_layout.html b/public/templates/artists/_show_layout.html
index 3cd2fdfa..238862e5 100644
--- a/public/templates/artists/_show_layout.html
+++ b/public/templates/artists/_show_layout.html
@@ -5,7 +5,7 @@
Comments
-
+
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
- What's Popular Today
-
-
+
\ 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 @@
+
\ 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 .
-
+
+ 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
+
+
+
+
+
\ 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 @@