diff --git a/Gemfile b/Gemfile
index edd8a46..2c27471 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,6 +6,9 @@ gem 'json'
gem 'sass'
gem 'coffee-script'
+gem 'therubyracer', :require => false
+gem 'barista'
+
gem 'awesome_print', :require => 'ap'
gem 'delayed_job'
gem 'lastfm', :git => 'git://github.com/magnolia-fan/ruby-lastfm.git'
diff --git a/Gemfile.lock b/Gemfile.lock
index 2941804..5fc6176 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -47,6 +47,8 @@ GEM
activesupport (3.0.9)
arel (2.0.10)
awesome_print (0.4.0)
+ barista (1.2.1)
+ coffee-script (~> 2.2)
builder (2.1.2)
coffee-script (2.2.0)
coffee-script-source
@@ -66,6 +68,7 @@ GEM
crack (= 0.1.8)
i18n (0.5.0)
json (1.5.3)
+ libv8 (3.3.10.2)
mail (2.2.19)
activesupport (>= 2.3.6)
i18n (>= 0.4.0)
@@ -98,6 +101,8 @@ GEM
rdoc (3.6.1)
sass (3.1.3)
sqlite3 (1.3.3)
+ therubyracer (0.9.2)
+ libv8 (~> 3.3.10)
thor (0.14.6)
treetop (1.4.9)
polyglot (>= 0.3.1)
@@ -109,6 +114,7 @@ PLATFORMS
DEPENDENCIES
awesome_print
+ barista
coffee-script
delayed_job
json
@@ -118,3 +124,4 @@ DEPENDENCIES
rails (= 3.0.9)
sass
sqlite3
+ therubyracer
diff --git a/app/coffeescripts/ajax.coffee b/app/coffeescripts/ajax.coffee
new file mode 100644
index 0000000..270dded
--- /dev/null
+++ b/app/coffeescripts/ajax.coffee
@@ -0,0 +1,70 @@
+class Ajax
+
+ referer: false
+
+ loadArtistData: (name) ->
+ search.showSpinner()
+ name = name.split(' ').join('+')
+ $.get '/artist/' +name+ '/', (data) ->
+ if data.status?
+ if data.status is 'loading'
+ search.showArtistPics data.pics
+ setTimeout () ->
+ this.loadArtistData name
+ , 3000
+ else if data.status is 'corrected'
+ ajax.loadArtistData data.page
+ else if data.status is 'suggestions'
+ search.hideSpinner()
+ search.showSuggestions data.values
+ else if data.status == 'loading_failed'
+ search.hideSpinner()
+ search.showError()
+ else
+ this.setArchor '/artist/' +name+ '/'
+ pages.renderArtist data
+ beathaven.redrawScrollbar()
+
+ loadSearchPage: ->
+ $.get '/templates/search.html', (data) ->
+ this.setArchor '/search/'
+ pages.renderSearch data
+
+ loadSettingsPage: ->
+ $.get '/templates/settings.html', (data) ->
+ this.setArchor '/settings/'
+ pages.renderSettings data
+
+ load404Page: ->
+ $.get '/404.html', (data) ->
+ $('.data-container .inner').html data
+ beathaven.redrawScrollbar()
+
+ setArchor: (anchor) ->
+ @referer = this.getAnchor()
+ window.location.hash = '#' +anchor
+
+ getAnchor: () ->
+ window.location.hash.substring 1;
+
+ setTitle: (title) ->
+ document.title = title+ ' @ BeatHaven'
+
+ detectPage: () ->
+ if m = this.getAnchor().match /\/artist\/(.+)\//
+ this.loadArtistData m[1]
+ else if this.getAnchor() == '' or Ajax.getAnchor().match /\/search\//
+ this.loadSearchPage();
+ else if this.getAnchor().match /\/settings\//
+ this.loadSearchPage()
+ else
+ this.load404Page()
+
+
+$ ->
+ window.ajax = new Ajax()
+ $('a.data.artist').live 'click', ->
+ ajax.loadArtistData $(this).html()
+ false
+ $(window).bind 'hashchange', ->
+ ajax.detectPage()
diff --git a/app/coffeescripts/beathaven.coffee b/app/coffeescripts/beathaven.coffee
new file mode 100644
index 0000000..a7bd775
--- /dev/null
+++ b/app/coffeescripts/beathaven.coffee
@@ -0,0 +1,76 @@
+$ ->
+ l = document.location
+ if l.host not in ['beathaven.org', 'localhost']
+ l.href = 'http://beathaven.org/'+ l.hash
+
+ window.beathaven = new BeatHaven()
+ window.vkontakte = new Vkontakte(if l.host == 'beathaven.org' then 2335068 else 2383163)
+
+ beathaven.init()
+ vkontakte.init()
+
+ $(window).resize ->
+ beathaven.adjustSizes()
+ window.setTimeout ->
+ beathaven.checkRedrawScrollbar()
+ , 500
+
+class BeatHaven
+
+ last_height: false
+
+ init: ->
+ this.drawInterface()
+ this.adjustSizes()
+ ajax.detectPage()
+
+ adjustSizes: ->
+ $('.data-container').height $(window).height() - $('.header-container').height()
+ $('.data-container').width $(window).width() - $('.player').width()
+ $('.player-container').height $(window).height()
+ $('.playlist').height $(window).height() - $('.player').height() - $('.player-container .additional-controls').height()
+
+ $('.data-container').scrollbar()
+ $('.playlist').scrollbar()
+
+ checkRedrawScrollbar: ->
+ focused_id = false
+ if document.activeElement.id?
+ focused_id = document.activeElement.id;
+ outer_height = $('.data-container > div').outerHeight()
+ if outer_height > 300 and outer_height != @last_height
+ @last_height = outer_height
+ this.redrawScrollbar()
+ if focused_id
+ document.getElementById(focused_id).focus()
+ focused_id = false
+ window.setTimeout ->
+ beathaven.checkRedrawScrollbar()
+ , 500
+
+ redrawScrollbar: ->
+ $('.data-container').html $('.data-container').find('.inner').first()
+ $('.data-container').scrollbar()
+
+
+String::htmlsafe = ->
+ replaces = [
+ ["\\", "\\\\"]
+ ["\"", """]
+ ["<", "<"]
+ [">", ">"]
+ ]
+ str = this
+ for item in replaces
+ str = str.replace item[0], item[1]
+ str
+
+String::trim = ->
+ str = this
+ while str.indexOf(' ') != -1
+ str = str.replace(' ', ' ')
+ if str.charAt(0) == ' '
+ str = str.substring 1
+ if str.charAt(str.length - 1) == ' '
+ str = str.substring(0, str.length - 1)
+ str
diff --git a/app/coffeescripts/pages.coffee b/app/coffeescripts/pages.coffee
new file mode 100644
index 0000000..a364b3a
--- /dev/null
+++ b/app/coffeescripts/pages.coffee
@@ -0,0 +1,88 @@
+class Pages
+
+ renderArtist: (data) ->
+ artist_info = $ '
+
+
+
+
+
' +data.artist.name+ '
+
+ ' +data.artist.desc+ '
+
+
'
+
+ albums_info = $ ''
+ $.each data.albums, (i, album) ->
+ if album.year?
+ album_info = $ '
+
+
' +album.name+ ' (' +album.year+ ')
+
+
+
+
+
+
+ '
+
+ $.each album.tracks.album, (i, track) ->
+ track_info = $ '
+
+ +
+
+
+
' +(i+1)+ '
+
' +track.name+ '
+
' +track.duration+ '
+
+ '
+
+ $(album_info).find('.tracklist ul').append(track_info)
+
+ $(albums_info).append(album_info)
+
+ $('.data-container').css backgroundImage: 'none'
+ $('.data-container .inner').html('').append(artist_info).append(albums_info)
+
+ yaCounter7596904.hit ajax.getAnchor(), data.artist.name, ajax.referer
+ ajax.setTitle data.artist.name
+
+ renderSearch: (data) ->
+ $('.data-container').css background: 'url(/images/concrete_wall_2.png) 0 -30px repeat'
+ $('.data-container .inner').html data
+
+ $('.search-container')
+ .css('marginLeft', ($('.data-container').width() - 425) / 2 + 'px')
+ .css('marginTop', ($('.data-container').height() / 2 - 230)+ 'px')
+ .height(($('.data-container').height() - $('#search_form').height()) / 2)
+
+ setTimeout ->
+ $('#search_field').bh_autocomplete
+ serviceUrl: '/artist/autocomplete' # Страница для обработки запросов автозаполнения
+ minChars: 2 # Минимальная длина запроса для срабатывания автозаполнения
+ delimiter: /(,|;)\s*/ # Разделитель для нескольких запросов, символ или регулярное выражение
+ maxHeight: 400 # Максимальная высота списка подсказок, в пикселях
+ width: 415 # Ширина списка
+ zIndex: 9999 # z-index списка
+ deferRequestBy: 500 # Задержка запроса (мсек)
+ onSelect: ->
+ ajax.loadArtistData()
+ $('#search_field').focus()
+ , 501
+
+ yaCounter7596904.hit ajax.getAnchor(), 'Artist Search', ajax.referer
+ ajax.setTitle 'Artist Search'
+
+ renderSettings: (data) ->
+ $('.data-container').css background: 'none'
+ $('.data-container .inner').html data
+ yaCounter7596904.hit ajax.getAnchor(), 'Settings', ajax.referer
+ ajax.setTitle 'Settings'
+ $('.settings-container .tabs .tab').first().trigger 'click'
+
+$ ->
+ window.pages = new Pages()
+ false
\ No newline at end of file
diff --git a/app/coffeescripts/player.coffee b/app/coffeescripts/player.coffee
new file mode 100644
index 0000000..2363970
--- /dev/null
+++ b/app/coffeescripts/player.coffee
@@ -0,0 +1,245 @@
+class Player
+
+ bar_width: 330
+ jp: null
+ scrobbled: false
+
+ initJplayer: ->
+ self = this
+
+ @jp = $("#jplayer")
+ @jp.jPlayer
+ swfPath: "/js"
+ supplied: "mp3"
+ cssSelectorAncestor: ""
+ cssSelector:
+ play: "#player .play"
+ pause: "#player .pause"
+ stop: ""
+ videoPlay: ""
+ seekBar: ""
+ playBar: ""
+ mute: ""
+ unmute: ""
+ volumeBar: ""
+ volumeBarValue: ""
+ currentTime: ""
+ duration: ""
+
+ @jp.bind $.jPlayer.event.timeupdate, (e) ->
+ data = e.jPlayer.status
+ if not Player.scrobbled and data.currentPercentAbsolute > 50
+ $obj = $('.playlist-tracks li.now')
+ self.scrobble $obj.attr('data-artist'), $obj.attr('data-album'), $obj.attr('data-track')
+ Player.scrobbled = true
+ $('#player .progress .loaded').width(data.seekPercent * self.bar_width / 100)
+ $('#player .progress .played').width(data.currentPercentAbsolute * self.bar_width / 100)
+
+ @jp.bind $.jPlayer.event.ended, (e) ->
+ next = self.nextTrack()
+ if not next
+ $('#jplayer').jPlayer 'clearMedia'
+ $('#player .now-playing').html 'Nothing left to lose play'
+ $('#player .loaded, #player .played').width 0
+ $('.playlist-tracks li').removeClass 'now'
+ else
+ self.setTrack next
+ false
+
+ addTrack: (artist, album, track, length, autoplay) ->
+ if not autoplay?
+ autoplay = false
+ initial_count = $('.playlist-tracks li').length
+ $('.playlist-tracks').append '
+
+
+
+
' +artist+ ' &mdash ' +track+ '
+
' +length+ '
+
remove
+
+ '
+
+ $('#playlist').html($('.playlist-tracks')).scrollbar()
+ $('.playlist-tracks').sortable()
+
+ if autoplay
+ Player.setTrack($('.playlist-tracks li').last().attr('id').split('i')[1])
+ else if initial_count == 0 and not Player.hasTrack()
+ Player.setTrack($('.playlist-tracks li').first().attr('id').split('i')[1])
+
+ setTrack: (id) ->
+ $obj = $('#i' +id)
+ query = $obj.attr('data-artist')+ ' &mdash ' +$obj.attr('data-track')
+
+ $('#player .now-playing').html query+ ''
+ $('.playlist-tracks li').removeClass 'now'
+ $obj.addClass 'now'
+ $('#player .loaded, #player .played').width 0
+
+ vkontakte.loadTracksData $obj.attr('data-artist'), $obj.attr('data-track'), $obj.attr('data-length'), ->
+ player.playSource()
+ this.updateNowListening $obj.attr('data-artist'), $obj.attr('data-album'), $obj.attr('data-track')
+
+ hasTrack: ->
+ if $('#jplayer audio').length > 0
+ return $('#jplayer audio').attr('src')?
+ else if $('#jplayer object').length > 0
+ $('#jplayer').jPlayer 'play'
+ true
+ false
+
+ playSource: (url) ->
+ $('#jplayer').jPlayer 'setMedia', mp3: url
+ $('#jplayer').jPlayer 'play'
+ @scrobbled = false
+
+ nextTrack: (manual) ->
+ manual = manual?
+ cnt = $('.playlist-tracks li').length
+ if not this.onShuffle() # Shuffle off
+ if $('.playlist-tracks .now').next().length == 0 # Last track and repeat is on
+ if Player.onRepeat() or manual # Repeat or manual click
+ return $('.playlist-tracks li').first().attr('id').split('i')[1]
+ else
+ false
+ else
+ return $('.playlist-tracks .now').next().attr('id').split('i')[1]
+ else if cnt == 1 # Single track in the playlist
+ return $('.playlist-tracks li').first().attr('id').split('i')[1]
+ else # Shuffle on
+ while true
+ rnd = Math.floor(Math.random() * (cnt + .999))
+ $li = $('.playlist-tracks li').eq rnd
+ if $li.length > 0 and not $li.hasClass 'now'
+ return $li.attr('id').split('i')[1]
+ false
+
+ prevTrack: ->
+ cnt = $('.playlist-tracks li').length
+ if not Player.onShuffle() # Shuffle off
+ if $('.playlist-tracks .now').prev().length == 0 # First track in the playlist
+ return $('.playlist-tracks li').last().attr('id').split('i')[1]
+ else
+ return $('.playlist-tracks .now').prev().attr('id').split('i')[1]
+ else if cnt == 1 # Single track in the playlist
+ return $('.playlist-tracks li').first().attr('id').split('i')[1]
+ else # Shuffle on
+ while true
+ rnd = Math.floor(Math.random() * (cnt + .999))
+ $li = $('.playlist-tracks li').eq rnd
+ if $li.length > 0 and not $li.hasClass 'now'
+ return $li.attr('id').split('i')[1]
+ false
+
+ onShuffle: ->
+ return $('#shuffle').hasClass 'active'
+
+ onRepeat: ->
+ return $('#repeat').hasClass 'active'
+
+ updateNowListening: (artist, album, track) ->
+ if session.user.lastfm_username
+ session.query '/lastfm/listening?r=' +Math.random(), artist: artist, album: album, track: track
+
+ scrobble: (artist, album, track) ->
+ if session.user.lastfm_username
+ session.query '/lastfm/scrobble?r=' +Math.random(), artist: artist, album: album, track: track
+
+
+$ ->
+ window.player = new Player()
+ window.player.initJplayer()
+ false
+
+
+# Player Controls
+
+$('#player .controls .prev').live 'click', ->
+ Player.setTrack Player.prevTrack()
+ false
+
+$('#player .controls .next').live 'click', ->
+ Player.setTrack Player.nextTrack(true)
+ false
+
+$('#player .play').live 'click', ->
+ if $('.playlist-tracks li').length > 0 and not Player.hasTrack()
+ player.setTrack $('.playlist-tracks li').first().attr('id').split('i')[1]
+ false
+
+$('#player .progress').live 'click', (e) ->
+ $('#jplayer').jPlayer 'playHead', Math.round((e.offsetX / Player.bar_width) * 100)
+ false
+
+# Player Additional Controls
+
+$('#repeat, #shuffle').live 'click', ->
+ $(this).toggleClass 'active'
+ false
+
+$('#empty-playlist').live 'click', ->
+ if confirm('Are you sure?')
+ $('.playlist-tracks li').remove()
+ $('#jplayer').jPlayer 'clearMedia'
+ $('#player .now-playing').text 'Add some music to playlist'
+ $('#player .loaded, #player .played').width 0
+ false
+
+# Playlist Actions
+
+$('.playlist-tracks li .fade, .playlist-tracks li .duration, .playlist-tracks li .remove').live 'mouseover mouseout', (e) ->
+ if e.type == 'mouseover'
+ $(this).parent().find('.duration').hide()
+ $(this).parent().find('.remove').show()
+ else
+ $(this).parent().find('.remove').hide()
+ $(this).parent().find('.duration').show()
+ false
+
+$('.playlist-tracks li .remove').live 'click', ->
+ $li = $(this).parent().parent()
+ if $li.hasClass 'now'
+ $('#jplayer').jPlayer 'clearMedia'
+ $('#player .now-playing').text '...'
+ $('#player .loaded, #player .played').width 0
+ $li.remove()
+ false
+
+$('.playlist-tracks li').live 'dblclick', ->
+ player.setTrack $(this).attr('id').split('i')[1]
+ false
+
+# Adding To Playlist actions
+
+$('.add-album').live 'click', ->
+ artist = $('.artist-info .name').html()
+ album = $(this).parent().parent().parent().find('h2.name').text().replace /\s\([\d]{4}\)$/, ''
+ for item in $(this).parent().parent().parent().find('.tracklist li')
+ track_name = $(item).find('.trackname').html()
+ length = $(item).find('.length').html()
+ Player.addTrack artist, album, track_name, length
+ false
+
+$('.add-track').live 'click', ->
+ artist = $('.artist-info .name').html()
+ album = $(this).parent().parent().parent().parent().find('h2.name').text().replace /\s\([\d]{4}\)$/, ''
+ track_name = $(this).parent().find('.trackname').html()
+ length = $(this).parent().find('.length').html()
+ Player.addTrack artist, album, track_name, length
+ false
+
+$('.tracklist li').live 'mouseover mouseout', (e) ->
+ if e.type == 'mouseover'
+ $(this).find('.add-track').show()
+ else
+ $(this).find('.add-track').hide()
+ false
+
+$('.tracklist li').live 'dblclick', (e) ->
+ artist = $('.artist-info .name').html()
+ album = $(this).parent().parent().parent().find('h2.name').text().replace /\s\([\d]{4}\)$/, ''
+ track_name = $(this).find('.trackname').html()
+ length = $(this).find('.length').html()
+ Player.addTrack artist, album, track_name, length, true
+ false
diff --git a/app/coffeescripts/search.coffee b/app/coffeescripts/search.coffee
new file mode 100644
index 0000000..d096fe4
--- /dev/null
+++ b/app/coffeescripts/search.coffee
@@ -0,0 +1,64 @@
+class Search
+
+ pics: []
+
+ showSpinner: ->
+ $('.search-container input').attr(disabled: 'disabled').blur()
+ $('.search-container img').show()
+ $('.autocomplete-container').hide()
+ $('.artist_loading.failed').hide()
+ this.hideSuggestions()
+ false
+
+ hideSpinner: ->
+ $('.search-container input').removeAttr 'disabled'
+ $('.search_field').focus()
+ $('.search-container img').hide()
+ false
+
+ showSuggestions: (values) ->
+ for item in values
+ $('.suggestions ul').append '
+
+ ' +item.name+ '
+ ' +(if item.desc then '
'+ item.desc +'' else '')+ '
+ '
+ $('.suggestions').show()
+ false
+
+ hideSuggestions: ->
+ $('.suggestions ul li').remove()
+ $('.suggestions').hide()
+ false
+
+ showArtistPics: (pics) ->
+ $('.artist_loading.ok, .artist_pics').show()
+ for pic in pics
+ if @pics.indexOf(pic) == -1
+ @pics.push(pic);
+ $('.artist_pics').append '
+
+
+
'
+ false
+
+ showError: ->
+ $('.artist_loading.ok, .artist_pics').hide()
+ $('.artist_loading.failed').show()
+ @pics = []
+
+
+$ ->
+ window.search = new Search()
+ false
+
+$('.search').live 'click', ->
+ ajax.loadSearchPage()
+ false
+$('#search_form').live 'submit', ->
+ $('.autocomplete-container').remove()
+ ajax.loadArtistData $('#search_field').val()
+ false
+$('.suggestions a').live 'click', ->
+ $('#search_field').val $(this).text()
+ false
diff --git a/app/coffeescripts/session.coffee b/app/coffeescripts/session.coffee
new file mode 100644
index 0000000..0b43905
--- /dev/null
+++ b/app/coffeescripts/session.coffee
@@ -0,0 +1,23 @@
+class Session
+
+ vk_params: null
+ user: null
+
+ constructor: (@vk_params) ->
+
+ query: (url, params, callback) ->
+ q_params = @vk_params
+ for attr in params
+ q_params[attr] = params[attr]
+ $.post url, q_params, callback
+ false
+
+ setVkParams: (params) ->
+ attrs = ['expire', 'mid', 'secret', 'sid', 'sig']
+ for key in attrs
+ @vk_params[key] = params[key]
+ false
+
+$ ->
+ window.session = new Session()
+ false
\ No newline at end of file
diff --git a/app/coffeescripts/settings.coffee b/app/coffeescripts/settings.coffee
new file mode 100644
index 0000000..ff05e4b
--- /dev/null
+++ b/app/coffeescripts/settings.coffee
@@ -0,0 +1,41 @@
+class Settings
+
+ getAccountInfo: (callback) ->
+ session.query '/user/update/', {}, callback
+
+ saveAccountInfo: (params, callback) ->
+ session.query '/user/update', params, callback
+
+ loadFormData: (form) ->
+ if form == 'account'
+ $('.settings-container .form input[name$="username"]').val session.user.name
+ $('.settings-container .form input[name$="email"]').val session.email
+ else if form == 'lastfm'
+ if @user.lastfm_username
+ $('.form-container input[name$="username"]').first().val session.lastfm_username
+
+$ ->
+ window.settings = new Settings()
+ false
+
+$('.settings') .live 'click', ->
+ ajax.loadSettingsPage();
+
+$('.settings-container .tabs .tab').live 'click', ->
+ if $(this).hasClass 'active'
+ $('.settings-container .tabs .tab').removeClass 'active'
+ $(this).addClass 'active'
+ $('.form-container').html $('.forms .'+ $(this).attr 'data-fieldset').html()
+ settings.loadFormData $(this).attr 'data-fieldset'
+
+$('.lastfm-connect') .live 'click', ->
+ window.open session.user.lastfm_login_url
+
+$('.settings-container .form input').live 'blur', ->
+ active_tab = $('.settings-container .tabs .tab.active').attr 'data-fieldset'
+ if active_tab == 'account'
+ params =
+ username: $('.settings-container .form input[name$="username"]').first().val()
+ email: $('.settings-container .form input[name$="email"]').first().val()
+ settings.saveAccountInfo params, ->
+ $('#header-container .hello .greating').text 'Hi there, ' +(if params.username.length > 0 then params.username else '%username%')+ '!'
\ No newline at end of file
diff --git a/app/coffeescripts/vkontakte.coffee b/app/coffeescripts/vkontakte.coffee
new file mode 100644
index 0000000..ed0de1a
--- /dev/null
+++ b/app/coffeescripts/vkontakte.coffee
@@ -0,0 +1,92 @@
+class Vkontakte
+
+ qr: []
+
+ constructor: (@api_id) ->
+
+ init: ->
+ VK.init
+ apiId: @api_id
+ nameTransportPath: '/xd_receiver.html'
+
+ VK.Auth.getLoginStatus this.authInfo
+
+ authInfo: (response) ->
+ if typeof response isnt 'undefined' and response.session
+ session.setVkParams response.session
+
+ $('#vk_login, .auth_notice').hide()
+ $('#vk_logout').css display: 'block'
+
+ $('#search_field').focus() if $('#search_field').length > 0
+
+ session.query '/user/auth', {}, (ar) ->
+ if ar.newbie
+ VK.Api.call 'getVariable', key: 1281, (r) ->
+ Session.query '/user/update', name: r.response, (ar2) ->
+ session.user = ar2.user
+ $('#header-container .hello .greating')
+ .text 'Hi there, ' +(if session.user.name then session.user.name else '%username%')+ '!'
+ else
+ session.user = ar.user
+
+ $('#header-container .hello .greating')
+ .text 'Hi there, ' +( if session.user.name then session.user.name else '%username%')+ '!'
+ else
+ $('#vk_login, .auth_notice').css display: 'block'
+ $('#vk_logout').hide()
+
+ loadTracksData: (artist, track, duration, callback) ->
+ track_prepared = track.replace(/\(.*\)/i, '').split('/')[0];
+ query = artist +' '+ track_prepared;
+ if @qr[query]?
+ callback @qr[query]
+ else
+ VK.Api.call 'audio.search', q: query, (r) ->
+ url = this.matchPerfectResult r.response, artist, track, duration
+ @qr[query] = url;
+ callback url
+
+ matchPerfectResult: (data, artist, track, duration) ->
+ duration = duration.split ':'
+
+ duration = parseInt(duration[0], 10) * 60 + parseInt(duration[1], 10)
+ best_score = 0;
+ best_result = null;
+ for item in data
+ score = 0;
+ item.artist = item.artist.trim();
+ item.title = item.title.trim();
+
+ if item.artist == artist
+ score += 10
+ else if item.artist.split(artist).length is 2
+ score += 5
+ else if item.title.split(artist).length is 2
+ score += 4
+
+ if item.title == track
+ score += 10
+ else if item.title.split(track).length is 2
+ score += 5
+
+ if parseInt(item.duration, 10) == duration
+ score += 15
+ else
+ delta = Math.abs parseInt(item.duration, 10) - duration
+ score += (10 - delta) if delta < 10
+
+ if score > best_score
+ best_score = score
+ best_result = item
+
+ if score is 35
+ return best_result.url
+
+ return best_result.url
+
+$ ->
+ $('#vk_login').click ->
+ VK.Auth.login vkontakte.authInfo, 8
+ $('#vk_logout').click ->
+ VK.Auth.logout vkontakte.authInfo
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index cba5ff7..743c62c 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -23,4 +23,8 @@ class ApplicationController < ActionController::Base
end
end
+ def index
+
+ end
+
end
diff --git a/public/css/.gitkeep b/app/views/application/index.html.erb
similarity index 100%
rename from public/css/.gitkeep
rename to app/views/application/index.html.erb
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 12df4b4..31c4f58 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -1,14 +1,80 @@
-
- Beathaven
- <%= stylesheet_link_tag :all %>
- <%= javascript_include_tag :defaults %>
- <%= csrf_meta_tag %>
-
-
+
+ BetaHaven
+
+
+
+
+ <%= javascript_include_tag "jquery/jquery.min.js" %>
+ <%= javascript_include_tag "jquery/jquery-ui/js/jquery-ui-1.8.13.custom.min.js" %>
+ <%= javascript_include_tag "jquery/jquery.autocomplete.js" %>
+ <%= javascript_include_tag "jquery/jquery.contentchange.js" %>
+ <%= javascript_include_tag "jquery/jquery.jplayer.js" %>
+ <%= javascript_include_tag "jquery/jquery.scroll.js" %>
-<%= yield %>
+ <%= javascript_include_tag "coffee/player.js", :type => "text/javascript", :charset => "utf-8" %>
+ <%= javascript_include_tag "coffee/search.js", :type => "text/javascript", :charset => "utf-8" %>
+ <%= javascript_include_tag "coffee/pages.js", :type => "text/javascript", :charset => "utf-8" %>
+ <%= javascript_include_tag "coffee/settings.js", :type => "text/javascript", :charset => "utf-8" %>
+
+
+
+ <%= yield %>
+
+
+
+
+
Add some music to playlist
+
+
+
+
+
+
+
Repeat
+
+
+
Shuffle
+
+
+
Empty Playlist
+
+
+
+
+
-
+
+ ↑
Don't forget to log in, please. It's simple.
+
+
+
+
+
diff --git a/config/initializers/barista_config.rb b/config/initializers/barista_config.rb
new file mode 100644
index 0000000..bd5dff1
--- /dev/null
+++ b/config/initializers/barista_config.rb
@@ -0,0 +1,64 @@
+# Configure barista.
+Barista.configure do |c|
+
+ # Change the root to use app/scripts
+ c.root = Rails.root.join("app", "coffeescripts")
+
+ # Change the output root, causing Barista to compile into public/coffeescripts
+ c.output_root = Rails.root.join("public", "javascripts", "coffee")
+ #
+ # Disable auto compile, use generated file directly:
+ # c.auto_compile = false
+
+ # Add a new framework:
+
+ # c.register :tests, :root => Rails.root.join('test', 'coffeescript'), :output_prefix => 'test'
+
+ # Disable wrapping in a closure:
+ # c.bare = true
+ # ... or ...
+ # c.bare!
+
+ # Change the output root for a framework:
+
+ # c.change_output_prefix! 'framework-name', 'output-prefix'
+
+ # or for all frameworks...
+
+ # c.each_framework do |framework|
+ # c.change_output_prefix! framework, "vendor/#{framework.name}"
+ # end
+
+ # or, prefix the path for the app files:
+
+ # c.change_output_prefix! :default, 'my-app-name'
+
+ # or, change the directory the framework goes into full stop:
+
+ # c.change_output_prefix! :tests, Rails.root.join('spec', 'javascripts')
+
+ # or, hook into the compilation:
+
+ # c.before_compilation { |path| puts "Barista: Compiling #{path}" }
+ # c.on_compilation { |path| puts "Barista: Successfully compiled #{path}" }
+ # c.on_compilation_error { |path, output| puts "Barista: Compilation of #{path} failed with:\n#{output}" }
+ # c.on_compilation_with_warning { |path, output| puts "Barista: Compilation of #{path} had a warning:\n#{output}" }
+
+ # Turn off preambles and exceptions on failure:
+
+ # c.verbose = false
+
+ # Or, make sure it is always on
+ # c.verbose!
+
+ # If you want to use a custom JS file, you can as well
+ # e.g. vendoring CoffeeScript in your application:
+ # c.js_path = Rails.root.join('public', 'javascripts', 'coffee-script.js')
+
+ # Make helpers and the HAML filter output coffee-script instead of the compiled JS.
+ # Used in combination with the coffeescript_interpreter_js helper in Rails.
+ # c.embedded_interpreter = true
+
+ c.add_preamble = false
+
+end
diff --git a/config/routes.rb b/config/routes.rb
index 614cb29..37e66ec 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -56,6 +56,8 @@ Beathaven::Application.routes.draw do
# Note: This route will make all actions in every controller accessible via GET requests.
# match ':controller(/:action(/:id(.:format)))'
+ match '/' => 'application#index'
+
match 'user/auth' => 'user#auth'
match 'user/update' => 'user#update'
diff --git a/public/index.html b/public/index.html
deleted file mode 100644
index 7f0606b..0000000
--- a/public/index.html
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
- BetaHaven
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Add some music to playlist
-
-
-
-
-
-
-
Repeat
-
-
-
Shuffle
-
-
-
Empty Playlist
-
-
-
-
-
-
-
- ↑
Don't forget to log in, please. It's simple.
-
-
-
-
-
-
diff --git a/public/js/Jplayer.swf b/public/javascripts/Jplayer.swf
similarity index 100%
rename from public/js/Jplayer.swf
rename to public/javascripts/Jplayer.swf
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
new file mode 100644
index 0000000..e69de29
diff --git a/public/javascripts/coffee/ajax.js b/public/javascripts/coffee/ajax.js
new file mode 100644
index 0000000..0a9d412
--- /dev/null
+++ b/public/javascripts/coffee/ajax.js
@@ -0,0 +1,87 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 17:43:40 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/ajax.coffee
+ */
+
+(function() {
+ var Ajax;
+ Ajax = (function() {
+ function Ajax() {}
+ Ajax.prototype.referer = false;
+ Ajax.prototype.loadArtistData = function(name) {
+ search.showSpinner();
+ name = name.split(' ').join('+');
+ return $.get('/artist/' + name + '/', function(data) {
+ if (data.status != null) {
+ if (data.status === 'loading') {
+ search.showArtistPics(data.pics);
+ return setTimeout(function() {
+ return this.loadArtistData(name);
+ }, 3000);
+ } else if (data.status === 'corrected') {
+ return ajax.loadArtistData(data.page);
+ } else if (data.status === 'suggestions') {
+ search.hideSpinner();
+ return search.showSuggestions(data.values);
+ } else if (data.status === 'loading_failed') {
+ search.hideSpinner();
+ return search.showError();
+ }
+ } else {
+ this.setArchor('/artist/' + name + '/');
+ pages.renderArtist(data);
+ return beathaven.redrawScrollbar();
+ }
+ });
+ };
+ Ajax.prototype.loadSearchPage = function() {
+ return $.get('/templates/search.html', function(data) {
+ this.setArchor('/search/');
+ return pages.renderSearch(data);
+ });
+ };
+ Ajax.prototype.loadSettingsPage = function() {
+ return $.get('/templates/settings.html', function(data) {
+ this.setArchor('/settings/');
+ return pages.renderSettings(data);
+ });
+ };
+ Ajax.prototype.load404Page = function() {
+ return $.get('/404.html', function(data) {
+ $('.data-container .inner').html(data);
+ return beathaven.redrawScrollbar();
+ });
+ };
+ Ajax.prototype.setArchor = function(anchor) {
+ this.referer = this.getAnchor();
+ return window.location.hash = '#' + anchor;
+ };
+ Ajax.prototype.getAnchor = function() {
+ return window.location.hash.substring(1);
+ };
+ Ajax.prototype.setTitle = function(title) {
+ return document.title = title + ' @ BeatHaven';
+ };
+ Ajax.prototype.detectPage = function() {
+ var m;
+ if (m = this.getAnchor().match(/\/artist\/(.+)\//)) {
+ return this.loadArtistData(m[1]);
+ } else if (this.getAnchor() === '' || Ajax.getAnchor().match(/\/search\//)) {
+ return this.loadSearchPage();
+ } else if (this.getAnchor().match(/\/settings\//)) {
+ return this.loadSearchPage();
+ } else {
+ return this.load404Page();
+ }
+ };
+ return Ajax;
+ })();
+ $(function() {
+ $('a.data.artist').live('click', function() {
+ ajax.loadArtistData($(this).html());
+ return false;
+ });
+ return $(window).bind('hashchange', function() {
+ return ajax.detectPage();
+ });
+ });
+}).call(this);
diff --git a/public/javascripts/coffee/beathaven.js b/public/javascripts/coffee/beathaven.js
new file mode 100644
index 0000000..6cffaec
--- /dev/null
+++ b/public/javascripts/coffee/beathaven.js
@@ -0,0 +1,95 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 17:49:39 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/beathaven.coffee
+ */
+
+(function() {
+ var BeatHaven;
+ $(function() {
+ var ajax, beathaven, l, pages, player, search, session, settings, vkontakte, _ref;
+ l = document.location;
+ if ((_ref = l.host) !== 'beathaven.org' && _ref !== 'localhost') {
+ l.href = 'http://beathaven.org/' + l.hash;
+ }
+ beathaven = new BeatHaven();
+ vkontakte = new Vkontakte(l.host === 'beathaven.org' ? 2335068 : 2383163);
+ session = new Session();
+ ajax = new Ajax();
+ player = new Player();
+ search = new Search();
+ pages = new Pages();
+ settings = new Settings();
+ beathaven.init();
+ vkontakte.init();
+ $(window).resize(function() {
+ return beathaven.adjustSizes();
+ });
+ return window.setTimeout(function() {
+ return beathaven.checkRedrawScrollbar();
+ }, 500);
+ });
+ BeatHaven = (function() {
+ function BeatHaven() {}
+ BeatHaven.prototype.last_height = false;
+ BeatHaven.prototype.init = function() {
+ this.drawInterface();
+ this.adjustSizes();
+ return ajax.detectPage();
+ };
+ BeatHaven.prototype.adjustSizes = function() {
+ $('.data-container').height($(window).height() - $('.header-container').height());
+ $('.data-container').width($(window).width() - $('.player').width());
+ $('.player-container').height($(window).height());
+ $('.playlist').height($(window).height() - $('.player').height() - $('.player-container .additional-controls').height());
+ $('.data-container').scrollbar();
+ return $('.playlist').scrollbar();
+ };
+ BeatHaven.prototype.checkRedrawScrollbar = function() {
+ var focused_id, outer_height;
+ focused_id = false;
+ if (document.activeElement.id != null) {
+ focused_id = document.activeElement.id;
+ }
+ outer_height = $('.data-container > div').outerHeight();
+ if (outer_height > 300 && outer_height !== this.last_height) {
+ this.last_height = outer_height;
+ this.redrawScrollbar();
+ }
+ if (focused_id) {
+ document.getElementById(focused_id).focus();
+ focused_id = false;
+ }
+ return window.setTimeout(function() {
+ return beathaven.checkRedrawScrollbar();
+ }, 500);
+ };
+ BeatHaven.prototype.redrawScrollbar = function() {
+ $('.data-container').html($('.data-container').find('.inner').first());
+ return $('.data-container').scrollbar();
+ };
+ return BeatHaven;
+ })();
+ String.prototype.htmlsafe = function() {
+ var item, replaces, str, _i, _len;
+ replaces = [["\\", "\\\\"], ["\"", """], ["<", "<"], [">", ">"]];
+ str = this;
+ for (_i = 0, _len = replaces.length; _i < _len; _i++) {
+ item = replaces[_i];
+ str = str.replace(item[0], item[1]);
+ }
+ return str;
+ };
+ String.prototype.trim = function() {
+ var str;
+ str = this;
+ while (str.indexOf(' ') !== -1) {
+ str = str.replace(' ', ' ');
+ }
+ if (str.charAt(0) === ' ') {
+ str = str.substring(1);
+ }
+ if (str.charAt(str.length - 1) === ' ') {
+ str = str.substring(0, str.length - 1);
+ }
+ return str;
+ };
+}).call(this);
diff --git a/public/javascripts/coffee/pages.js b/public/javascripts/coffee/pages.js
new file mode 100644
index 0000000..d15f691
--- /dev/null
+++ b/public/javascripts/coffee/pages.js
@@ -0,0 +1,96 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 17:49:39 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/pages.coffee
+ */
+
+(function() {
+ var Pages;
+ Pages = (function() {
+ function Pages() {}
+ Pages.prototype.renderArtist = function(data) {
+ var albums_info, artist_info;
+ artist_info = $('\
+ \
+
\
+
\
+
\
+
' + data.artist.name + '
\
+
\
+ ' + data.artist.desc + '\
+
\
+
');
+ albums_info = $('');
+ $.each(data.albums, function(i, album) {
+ var album_info;
+ if (album.year != null) {
+ album_info = $('\
+ \
+
' + album.name + ' (' + album.year + ')
\
+
\
+
\
+
\
+
\
+
\
+
\
+ ');
+ $.each(album.tracks.album, function(i, track) {
+ var track_info;
+ track_info = $('\
+ \
+ +
\
+ \
+
\
+
' + (i + 1) + '\
+
' + track.name + '
\
+
' + track.duration + '
\
+
\
+ ');
+ return $(album_info).find('.tracklist ul').append(track_info);
+ });
+ return $(albums_info).append(album_info);
+ }
+ });
+ $('.data-container').css({
+ backgroundImage: 'none'
+ });
+ $('.data-container .inner').html('').append(artist_info).append(albums_info);
+ yaCounter7596904.hit(ajax.getAnchor(), data.artist.name, ajax.referer);
+ return ajax.setTitle(data.artist.name);
+ };
+ Pages.prototype.renderSearch = function(data) {
+ $('.data-container').css({
+ background: 'url(/images/concrete_wall_2.png) 0 -30px repeat'
+ });
+ $('.data-container .inner').html(data);
+ $('.search-container').css('marginLeft', ($('.data-container').width() - 425) / 2 + 'px').css('marginTop', ($('.data-container').height() / 2 - 230) + 'px').height(($('.data-container').height() - $('#search_form').height()) / 2);
+ setTimeout(function() {
+ $('#search_field').bh_autocomplete({
+ serviceUrl: '/artist/autocomplete',
+ minChars: 2,
+ delimiter: /(,|;)\s*/,
+ maxHeight: 400,
+ width: 415,
+ zIndex: 9999,
+ deferRequestBy: 500,
+ onSelect: function() {
+ return ajax.loadArtistData();
+ }
+ });
+ return $('#search_field').focus();
+ }, 501);
+ yaCounter7596904.hit(ajax.getAnchor(), 'Artist Search', ajax.referer);
+ return ajax.setTitle('Artist Search');
+ };
+ Pages.prototype.renderSettings = function(data) {
+ $('.data-container').css({
+ background: 'none'
+ });
+ $('.data-container .inner').html(data);
+ yaCounter7596904.hit(ajax.getAnchor(), 'Settings', ajax.referer);
+ ajax.setTitle('Settings');
+ return $('.settings-container .tabs .tab').first().trigger('click');
+ };
+ return Pages;
+ })();
+}).call(this);
diff --git a/public/javascripts/coffee/player.js b/public/javascripts/coffee/player.js
new file mode 100644
index 0000000..16893a2
--- /dev/null
+++ b/public/javascripts/coffee/player.js
@@ -0,0 +1,285 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 17:49:39 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/player.coffee
+ */
+
+(function() {
+ var Player;
+ Player = (function() {
+ function Player() {}
+ Player.prototype.bar_width = 330;
+ Player.prototype.jp = null;
+ Player.prototype.scrobbled = false;
+ Player.prototype.initJplayer = function() {
+ var self;
+ self = this;
+ this.jp = $("#jplayer");
+ this.jp.jPlayer({
+ swfPath: "/js",
+ supplied: "mp3",
+ cssSelectorAncestor: "",
+ cssSelector: {
+ play: "#player .play",
+ pause: "#player .pause",
+ stop: "",
+ videoPlay: "",
+ seekBar: "",
+ playBar: "",
+ mute: "",
+ unmute: "",
+ volumeBar: "",
+ volumeBarValue: "",
+ currentTime: "",
+ duration: ""
+ }
+ });
+ this.jp.bind($.jPlayer.event.timeupdate, function(e) {
+ var $obj, data;
+ data = e.jPlayer.status;
+ if (!Player.scrobbled && data.currentPercentAbsolute > 50) {
+ $obj = $('.playlist-tracks li.now');
+ self.scrobble($obj.attr('data-artist'), $obj.attr('data-album'), $obj.attr('data-track'));
+ Player.scrobbled = true;
+ }
+ $('#player .progress .loaded').width(data.seekPercent * self.bar_width / 100);
+ return $('#player .progress .played').width(data.currentPercentAbsolute * self.bar_width / 100);
+ });
+ this.jp.bind($.jPlayer.event.ended, function(e) {
+ var next;
+ next = self.nextTrack();
+ if (!next) {
+ $('#jplayer').jPlayer('clearMedia');
+ $('#player .now-playing').html('Nothing left to lose play');
+ $('#player .loaded, #player .played').width(0);
+ return $('.playlist-tracks li').removeClass('now');
+ } else {
+ return self.setTrack(next);
+ }
+ });
+ return false;
+ };
+ Player.prototype.addTrack = function(artist, album, track, length, autoplay) {
+ var initial_count;
+ if (!(autoplay != null)) {
+ autoplay = false;
+ }
+ initial_count = $('.playlist-tracks li').length;
+ $('.playlist-tracks').append('\
+ \
+ \
+
\
+
' + artist + ' &mdash ' + track + '\
+
' + length + '\
+
remove
\
+
\
+ ');
+ $('#playlist').html($('.playlist-tracks')).scrollbar();
+ $('.playlist-tracks').sortable();
+ if (autoplay) {
+ return Player.setTrack($('.playlist-tracks li').last().attr('id').split('i')[1]);
+ } else if (initial_count === 0 && !Player.hasTrack()) {
+ return Player.setTrack($('.playlist-tracks li').first().attr('id').split('i')[1]);
+ }
+ };
+ Player.prototype.setTrack = function(id) {
+ var $obj, query;
+ $obj = $('#i' + id);
+ query = $obj.attr('data-artist') + ' &mdash ' + $obj.attr('data-track');
+ $('#player .now-playing').html(query + '');
+ $('.playlist-tracks li').removeClass('now');
+ $obj.addClass('now');
+ $('#player .loaded, #player .played').width(0);
+ vkontakte.loadTracksData($obj.attr('data-artist'), $obj.attr('data-track'), $obj.attr('data-length'), function() {
+ return player.playSource();
+ });
+ return this.updateNowListening($obj.attr('data-artist'), $obj.attr('data-album'), $obj.attr('data-track'));
+ };
+ Player.prototype.hasTrack = function() {
+ if ($('#jplayer audio').length > 0) {
+ return $('#jplayer audio').attr('src') != null;
+ } else if ($('#jplayer object').length > 0) {
+ $('#jplayer').jPlayer('play');
+ true;
+ }
+ return false;
+ };
+ Player.prototype.playSource = function(url) {
+ $('#jplayer').jPlayer('setMedia', {
+ mp3: url
+ });
+ $('#jplayer').jPlayer('play');
+ return this.scrobbled = false;
+ };
+ Player.prototype.nextTrack = function(manual) {
+ var $li, cnt, rnd;
+ manual = manual != null;
+ cnt = $('.playlist-tracks li').length;
+ if (!this.onShuffle()) {
+ if ($('.playlist-tracks .now').next().length === 0) {
+ if (Player.onRepeat() || manual) {
+ return $('.playlist-tracks li').first().attr('id').split('i')[1];
+ } else {
+ false;
+ }
+ } else {
+ return $('.playlist-tracks .now').next().attr('id').split('i')[1];
+ }
+ } else if (cnt === 1) {
+ return $('.playlist-tracks li').first().attr('id').split('i')[1];
+ } else {
+ while (true) {
+ rnd = Math.floor(Math.random() * (cnt + .999));
+ $li = $('.playlist-tracks li').eq(rnd);
+ if ($li.length > 0 && !$li.hasClass('now')) {
+ return $li.attr('id').split('i')[1];
+ }
+ }
+ }
+ return false;
+ };
+ Player.prototype.prevTrack = function() {
+ var $li, cnt, rnd;
+ cnt = $('.playlist-tracks li').length;
+ if (!Player.onShuffle()) {
+ if ($('.playlist-tracks .now').prev().length === 0) {
+ return $('.playlist-tracks li').last().attr('id').split('i')[1];
+ } else {
+ return $('.playlist-tracks .now').prev().attr('id').split('i')[1];
+ }
+ } else if (cnt === 1) {
+ return $('.playlist-tracks li').first().attr('id').split('i')[1];
+ } else {
+ while (true) {
+ rnd = Math.floor(Math.random() * (cnt + .999));
+ $li = $('.playlist-tracks li').eq(rnd);
+ if ($li.length > 0 && !$li.hasClass('now')) {
+ return $li.attr('id').split('i')[1];
+ }
+ }
+ }
+ return false;
+ };
+ Player.prototype.onShuffle = function() {
+ return $('#shuffle').hasClass('active');
+ };
+ Player.prototype.onRepeat = function() {
+ return $('#repeat').hasClass('active');
+ };
+ Player.prototype.updateNowListening = function(artist, album, track) {
+ if (session.user.lastfm_username) {
+ return session.query('/lastfm/listening?r=' + Math.random(), {
+ artist: artist,
+ album: album,
+ track: track
+ });
+ }
+ };
+ Player.prototype.scrobble = function(artist, album, track) {
+ if (session.user.lastfm_username) {
+ return session.query('/lastfm/scrobble?r=' + Math.random(), {
+ artist: artist,
+ album: album,
+ track: track
+ });
+ }
+ };
+ return Player;
+ })();
+ $(function() {
+ return player.initJplayer();
+ });
+ $('#player .controls .prev').live('click', function() {
+ Player.setTrack(Player.prevTrack());
+ return false;
+ });
+ $('#player .controls .next').live('click', function() {
+ Player.setTrack(Player.nextTrack(true));
+ return false;
+ });
+ $('#player .play').live('click', function() {
+ if ($('.playlist-tracks li').length > 0 && !Player.hasTrack()) {
+ player.setTrack($('.playlist-tracks li').first().attr('id').split('i')[1]);
+ }
+ return false;
+ });
+ $('#player .progress').live('click', function(e) {
+ $('#jplayer').jPlayer('playHead', Math.round((e.offsetX / Player.bar_width) * 100));
+ return false;
+ });
+ $('#repeat, #shuffle').live('click', function() {
+ $(this).toggleClass('active');
+ return false;
+ });
+ $('#empty-playlist').live('click', function() {
+ if (confirm('Are you sure?')) {
+ $('.playlist-tracks li').remove();
+ $('#jplayer').jPlayer('clearMedia');
+ $('#player .now-playing').text('Add some music to playlist');
+ $('#player .loaded, #player .played').width(0);
+ }
+ return false;
+ });
+ $('.playlist-tracks li .fade, .playlist-tracks li .duration, .playlist-tracks li .remove').live('mouseover mouseout', function(e) {
+ if (e.type === 'mouseover') {
+ $(this).parent().find('.duration').hide();
+ $(this).parent().find('.remove').show();
+ } else {
+ $(this).parent().find('.remove').hide();
+ $(this).parent().find('.duration').show();
+ }
+ return false;
+ });
+ $('.playlist-tracks li .remove').live('click', function() {
+ var $li;
+ $li = $(this).parent().parent();
+ if ($li.hasClass('now')) {
+ $('#jplayer').jPlayer('clearMedia');
+ $('#player .now-playing').text('...');
+ $('#player .loaded, #player .played').width(0);
+ }
+ $li.remove();
+ return false;
+ });
+ $('.playlist-tracks li').live('dblclick', function() {
+ player.setTrack($(this).attr('id').split('i')[1]);
+ return false;
+ });
+ $('.add-album').live('click', function() {
+ var album, artist, item, length, track_name, _i, _len, _ref;
+ artist = $('.artist-info .name').html();
+ album = $(this).parent().parent().parent().find('h2.name').text().replace(/\s\([\d]{4}\)$/, '');
+ _ref = $(this).parent().parent().parent().find('.tracklist li');
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ item = _ref[_i];
+ track_name = $(item).find('.trackname').html();
+ length = $(item).find('.length').html();
+ Player.addTrack(artist, album, track_name, length);
+ }
+ return false;
+ });
+ $('.add-track').live('click', function() {
+ var album, artist, length, track_name;
+ artist = $('.artist-info .name').html();
+ album = $(this).parent().parent().parent().parent().find('h2.name').text().replace(/\s\([\d]{4}\)$/, '');
+ track_name = $(this).parent().find('.trackname').html();
+ length = $(this).parent().find('.length').html();
+ Player.addTrack(artist, album, track_name, length);
+ return false;
+ });
+ $('.tracklist li').live('mouseover mouseout', function(e) {
+ if (e.type === 'mouseover') {
+ $(this).find('.add-track').show();
+ } else {
+ $(this).find('.add-track').hide();
+ }
+ return false;
+ });
+ $('.tracklist li').live('dblclick', function(e) {
+ var album, artist, length, track_name;
+ artist = $('.artist-info .name').html();
+ album = $(this).parent().parent().parent().find('h2.name').text().replace(/\s\([\d]{4}\)$/, '');
+ track_name = $(this).find('.trackname').html();
+ length = $(this).find('.length').html();
+ Player.addTrack(artist, album, track_name, length, true);
+ return false;
+ });
+}).call(this);
diff --git a/public/javascripts/coffee/search.js b/public/javascripts/coffee/search.js
new file mode 100644
index 0000000..d9c056e
--- /dev/null
+++ b/public/javascripts/coffee/search.js
@@ -0,0 +1,81 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 17:49:39 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/search.coffee
+ */
+
+(function() {
+ var Search;
+ Search = (function() {
+ function Search() {}
+ Search.prototype.pics = [];
+ Search.prototype.showSpinner = function() {
+ $('.search-container input').attr({
+ disabled: 'disabled'
+ }).blur();
+ $('.search-container img').show();
+ $('.autocomplete-container').hide();
+ $('.artist_loading.failed').hide();
+ this.hideSuggestions();
+ return false;
+ };
+ Search.prototype.hideSpinner = function() {
+ $('.search-container input').removeAttr('disabled');
+ $('.search_field').focus();
+ $('.search-container img').hide();
+ return false;
+ };
+ Search.prototype.showSuggestions = function(values) {
+ var item, _i, _len;
+ for (_i = 0, _len = values.length; _i < _len; _i++) {
+ item = values[_i];
+ $('.suggestions ul').append('\
+ \
+ ' + item.name + '\
+ ' + (item.desc ? '
' + item.desc(+'') : '') + '\
+ ');
+ }
+ $('.suggestions').show();
+ return false;
+ };
+ Search.prototype.hideSuggestions = function() {
+ $('.suggestions ul li').remove();
+ $('.suggestions').hide();
+ return false;
+ };
+ Search.prototype.showArtistPics = function(pics) {
+ var pic, _i, _len;
+ $('.artist_loading.ok, .artist_pics').show();
+ for (_i = 0, _len = pics.length; _i < _len; _i++) {
+ pic = pics[_i];
+ if (this.pics.indexOf(pic) === -1) {
+ this.pics.push(pic);
+ $('.artist_pics').append('\
+ \
+
\
+
');
+ }
+ }
+ return false;
+ };
+ Search.prototype.showError = function() {
+ $('.artist_loading.ok, .artist_pics').hide();
+ $('.artist_loading.failed').show();
+ return this.pics = [];
+ };
+ return Search;
+ })();
+ $(function() {
+ $('.search').live('click', function() {
+ ajax.loadSearchPage();
+ return false;
+ });
+ $('#search_form').live('submit', function() {
+ $('.autocomplete-container').remove();
+ ajax.loadArtistData($('#search_field').val());
+ return false;
+ });
+ return $('.suggestions a').live('click', function() {
+ $('#search_field').val($(this).text());
+ return false;
+ });
+ });
+}).call(this);
diff --git a/public/javascripts/coffee/session.js b/public/javascripts/coffee/session.js
new file mode 100644
index 0000000..3ee7557
--- /dev/null
+++ b/public/javascripts/coffee/session.js
@@ -0,0 +1,34 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 17:49:39 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/session.coffee
+ */
+
+(function() {
+ var Session;
+ Session = (function() {
+ Session.prototype.vk_params = null;
+ Session.prototype.user = null;
+ function Session(vk_params) {
+ this.vk_params = vk_params;
+ }
+ Session.prototype.query = function(url, params, callback) {
+ var attr, q_params, _i, _len;
+ q_params = this.vk_params;
+ for (_i = 0, _len = params.length; _i < _len; _i++) {
+ attr = params[_i];
+ q_params[attr] = params[attr];
+ }
+ $.post(url, q_params, callback);
+ return false;
+ };
+ Session.prototype.setVkParams = function(params) {
+ var attrs, key, _i, _len;
+ attrs = ['expire', 'mid', 'secret', 'sid', 'sig'];
+ for (_i = 0, _len = attrs.length; _i < _len; _i++) {
+ key = attrs[_i];
+ this.vk_params[key] = params[key];
+ }
+ return false;
+ };
+ return Session;
+ })();
+}).call(this);
diff --git a/public/javascripts/coffee/settings.js b/public/javascripts/coffee/settings.js
new file mode 100644
index 0000000..ee5e1b5
--- /dev/null
+++ b/public/javascripts/coffee/settings.js
@@ -0,0 +1,54 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 14:00:02 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/settings.coffee
+ */
+
+(function() {
+ var Settings;
+ Settings = (function() {
+ function Settings() {}
+ Settings.prototype.getAccountInfo = function(callback) {
+ return session.query('/user/update/', {}, callback);
+ };
+ Settings.prototype.saveAccountInfo = function(params, callback) {
+ return session.query('/user/update', params, callback);
+ };
+ Settings.prototype.loadFormData = function(form) {
+ if (form === 'account') {
+ $('.settings-container .form input[name$="username"]').val(session.user.name);
+ return $('.settings-container .form input[name$="email"]').val(session.email);
+ } else if (form === 'lastfm') {
+ if (this.user.lastfm_username) {
+ return $('.form-container input[name$="username"]').first().val(session.lastfm_username);
+ }
+ }
+ };
+ return Settings;
+ })();
+ $('.settings').live('click', function() {
+ return ajax.loadSettingsPage();
+ });
+ $('.settings-container .tabs .tab').live('click', function() {
+ if ($(this).hasClass('active')) {
+ $('.settings-container .tabs .tab').removeClass('active');
+ $(this).addClass('active');
+ $('.form-container').html($('.forms .' + $(this).attr('data-fieldset')).html());
+ return settings.loadFormData($(this).attr('data-fieldset'));
+ }
+ });
+ $('.lastfm-connect').live('click', function() {
+ return window.open(session.user.lastfm_login_url);
+ });
+ $('.settings-container .form input').live('blur', function() {
+ var active_tab, params;
+ active_tab = $('.settings-container .tabs .tab.active').attr('data-fieldset');
+ if (active_tab === 'account') {
+ params = {
+ username: $('.settings-container .form input[name$="username"]').first().val(),
+ email: $('.settings-container .form input[name$="email"]').first().val()
+ };
+ return settings.saveAccountInfo(params, function() {
+ return $('#header-container .hello .greating').text('Hi there, ' + (params.username.length > 0 ? params.username : '%username%') + '!');
+ });
+ }
+ });
+}).call(this);
diff --git a/public/javascripts/coffee/vkontakte.js b/public/javascripts/coffee/vkontakte.js
new file mode 100644
index 0000000..3969f00
--- /dev/null
+++ b/public/javascripts/coffee/vkontakte.js
@@ -0,0 +1,121 @@
+/* DO NOT MODIFY. This file was compiled Mon, 27 Jun 2011 17:49:39 GMT from
+ * /Users/chez/Sites/beathaven/app/coffeescripts/vkontakte.coffee
+ */
+
+(function() {
+ var Vkontakte;
+ Vkontakte = (function() {
+ Vkontakte.prototype.qr = [];
+ function Vkontakte(api_id) {
+ this.api_id = api_id;
+ }
+ Vkontakte.prototype.init = function() {
+ VK.init({
+ apiId: this.api_id,
+ nameTransportPath: '/xd_receiver.html'
+ });
+ return VK.Auth.getLoginStatus(this.authInfo);
+ };
+ Vkontakte.prototype.authInfo = function(response) {
+ if (typeof response !== 'undefined' && response.session) {
+ session.setVkParams(response.session);
+ $('#vk_login, .auth_notice').hide();
+ $('#vk_logout').css({
+ display: 'block'
+ });
+ if ($('#search_field').length > 0) {
+ $('#search_field').focus();
+ }
+ return session.query('/user/auth', {}, function(ar) {
+ if (ar.newbie) {
+ VK.Api.call('getVariable', {
+ key: 1281
+ }, function(r) {
+ return Session.query('/user/update', {
+ name: r.response
+ }, function(ar2) {
+ session.user = ar2.user;
+ return $('#header-container .hello .greating').text('Hi there, ' + (session.user.name ? session.user.name : '%username%') + '!');
+ });
+ });
+ } else {
+ session.user = ar.user;
+ }
+ return $('#header-container .hello .greating').text('Hi there, ' + (session.user.name ? session.user.name : '%username%') + '!');
+ });
+ } else {
+ $('#vk_login, .auth_notice').css({
+ display: 'block'
+ });
+ return $('#vk_logout').hide();
+ }
+ };
+ Vkontakte.prototype.loadTracksData = function(artist, track, duration, callback) {
+ var query, track_prepared;
+ track_prepared = track.replace(/\(.*\)/i, '').split('/')[0];
+ query = artist(+' ' + track_prepared);
+ if (this.qr[query] != null) {
+ return callback(this.qr[query]);
+ } else {
+ return VK.Api.call('audio.search', {
+ q: query
+ }, function(r) {
+ var url;
+ url = this.matchPerfectResult(r.response, artist, track, duration);
+ this.qr[query] = url;
+ return callback(url);
+ });
+ }
+ };
+ Vkontakte.prototype.matchPerfectResult = function(data, artist, track, duration) {
+ var best_result, best_score, delta, item, score, _i, _len;
+ duration = duration.split(':');
+ duration = parseInt(duration[0], 10) * 60 + parseInt(duration[1], 10);
+ best_score = 0;
+ best_result = null;
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
+ item = data[_i];
+ score = 0;
+ item.artist = item.artist.trim();
+ item.title = item.title.trim();
+ if (item.artist === artist) {
+ score += 10;
+ } else if (item.artist.split(artist).length === 2) {
+ score += 5;
+ } else if (item.title.split(artist).length === 2) {
+ score += 4;
+ }
+ if (item.title === track) {
+ score += 10;
+ } else if (item.title.split(track).length === 2) {
+ score += 5;
+ }
+ if (parseInt(item.duration, 10) === duration) {
+ score += 15;
+ } else {
+ delta = Math.abs(parseInt(item.duration, 10) - duration);
+ if (delta < 10) {
+ score += 10 - delta;
+ }
+ }
+ if (score > best_score) {
+ best_score = score;
+ best_result = item;
+ }
+ if (score === 35) {
+ return best_result.url;
+ }
+ }
+ return best_result.url;
+ };
+ return Vkontakte;
+ })();
+ $(function() {
+ $('#vk_login').click(function() {
+ return VK.Auth.login(vkontakte.authInfo, 8);
+ });
+ return $('#vk_logout').click(function() {
+ return VK.Auth.logout(vkontakte.authInfo);
+ });
+ });
+}).call(this);
diff --git a/public/js/jquery/jquery-ui/js/jquery-ui-1.8.13.custom.min.js b/public/javascripts/jquery/jquery-ui/js/jquery-ui-1.8.13.custom.min.js
similarity index 100%
rename from public/js/jquery/jquery-ui/js/jquery-ui-1.8.13.custom.min.js
rename to public/javascripts/jquery/jquery-ui/js/jquery-ui-1.8.13.custom.min.js
diff --git a/public/js/jquery/jquery.autocomplete.js b/public/javascripts/jquery/jquery.autocomplete.js
similarity index 100%
rename from public/js/jquery/jquery.autocomplete.js
rename to public/javascripts/jquery/jquery.autocomplete.js
diff --git a/public/js/jquery/jquery.contentchange.js b/public/javascripts/jquery/jquery.contentchange.js
similarity index 100%
rename from public/js/jquery/jquery.contentchange.js
rename to public/javascripts/jquery/jquery.contentchange.js
diff --git a/public/js/jquery/jquery.jplayer.js b/public/javascripts/jquery/jquery.jplayer.js
similarity index 100%
rename from public/js/jquery/jquery.jplayer.js
rename to public/javascripts/jquery/jquery.jplayer.js
diff --git a/public/js/jquery/jquery.min.js b/public/javascripts/jquery/jquery.min.js
similarity index 100%
rename from public/js/jquery/jquery.min.js
rename to public/javascripts/jquery/jquery.min.js
diff --git a/public/js/jquery/jquery.scroll.js b/public/javascripts/jquery/jquery.scroll.js
similarity index 100%
rename from public/js/jquery/jquery.scroll.js
rename to public/javascripts/jquery/jquery.scroll.js
diff --git a/public/js/beathaven/ajax.js b/public/js/beathaven/ajax.js
deleted file mode 100644
index 43f7267..0000000
--- a/public/js/beathaven/ajax.js
+++ /dev/null
@@ -1,87 +0,0 @@
-var Ajax = {
-
- referer: false,
-
- loadArtistData: function(name) {
- Search.showSpinner();
- name = name.split(' ').join('+');
- $.get('/artist/'+ name +'/', function(data){
- if (typeof data.status != 'undefined') {
- if (data.status == 'loading') {
- Search.showArtistPics(data.pics);
- setTimeout(function() {
- Ajax.loadArtistData(name);
- }, 3000);
- } else if (data.status == 'corrected') {
- Ajax.loadArtistData(data.page);
- } else if (data.status == 'suggestions') {
- Search.hideSpinner();
- Search.showSuggestions(data.values);
- } else if (data.status == 'loading_failed') {
- Search.hideSpinner();
- Search.showError();
- }
- return false;
- } else {
- Ajax.setArchor('/artist/'+ name +'/');
- Pages.renderArtist(data);
- beathaven.redrawScrollbar();
- }
- })
- },
-
- loadSearchPage: function() {
- $.get('/templates/search.html', function(data){
- Ajax.setArchor('/search/');
- Pages.renderSearch(data);
- })
- },
-
- loadSettingsPage: function() {
- $.get('/templates/settings.html', function(data){
- Ajax.setArchor('/settings/');
- Pages.renderSettings(data);
- })
- },
-
- load404Page: function() {
- $.get('/404.html', function(data){
- $('.data-container .inner').html(data);
- beathaven.redrawScrollbar();
- })
- },
-
- setArchor: function(anchor) {
- Ajax.referer = Ajax.getAnchor();
- window.location.hash = '#'+ anchor;
- },
-
- getAnchor: function() {
- return window.location.hash.substring(1);
- },
-
- setTitle: function(title) {
- document.title = title +' @ BeatHaven';
- },
-
- detectPage: function() {
- if (m = Ajax.getAnchor().match(/\/artist\/(.+)\//)) {
- Ajax.loadArtistData(m[1]);
- } else if (Ajax.getAnchor() === '' || Ajax.getAnchor().match(/\/search\//)) {
- Ajax.loadSearchPage();
- } else if (Ajax.getAnchor().match(/\/settings\//)) {
- Ajax.loadSearchPage();
- } else {
- Ajax.load404Page();
- }
- }
-}
-
-
-$(function(){
- $('a.data.artist').live('click', function(){
- Ajax.loadArtistData($(this).html());
- return false;
- });
- //$(window).bind('hashchange', Ajax.detectPage);
-})
\ No newline at end of file
diff --git a/public/js/beathaven/layout.js b/public/js/beathaven/layout.js
deleted file mode 100644
index f9ee42c..0000000
--- a/public/js/beathaven/layout.js
+++ /dev/null
@@ -1,80 +0,0 @@
-$(function(){
- if (document.location.host != 'beathaven.org' && document.location.host != 'localhost') {
- document.location.href = 'http://beathaven.org/'+ document.location.hash;
- }
- beathaven.init();
- $(window).resize(beathaven.adjustSizes)
- window.setTimeout(beathaven.checkRedrawScrollbar, 500);
-})
-var beathaven = {
-
- last_height: false,
-
- init: function () {
- this.drawInterface();
- this.adjustSizes();
- Ajax.detectPage();
- },
-
- drawInterface: function() {
- },
-
- adjustSizes: function() {
- $('.data-container').height($(window).height() - $('.header-container').height());
- $('.data-container').width($(window).width() - $('.player').width()).scrollbar();
- $('.player-container').height($(window).height());
- $('.playlist').height($(window).height() - $('.player').height() - $('.player-container .additional-controls').height());
-
- $('.playlist').scrollbar();
- },
-
- checkRedrawScrollbar: function() {
- var focused_id = false;
- if (typeof document.activeElement.id !== 'undefined') {
- focused_id = document.activeElement.id;
- }
- var outer_height = $('.data-container > div').outerHeight();
- if (outer_height > 300 && outer_height != beathaven.last_height) {
- beathaven.last_height = outer_height;
- beathaven.redrawScrollbar();
- }
- if (focused_id) {
- document.getElementById(focused_id).focus();
- focused_id = false;
- }
- window.setTimeout(beathaven.checkRedrawScrollbar, 500);
- },
-
- redrawScrollbar: function() {
- $('.data-container').html($('.data-container').find('.inner').first());
- $('.data-container').scrollbar();
- }
-}
-
-String.prototype.htmlsafe = function() {
- var replaces = [
- ["\\", "\\\\"],
- ["\"", """],
- ["<", "<"],
- [">", ">"]
- ];
- var str = this;
- for (var i = 0; i < replaces.length; i++) {
- str = str.replace(replaces[i][0], replaces[i][1]);
- }
- return str;
-}
-
-String.prototype.trim = function() {
- var str = this;
- while (str.indexOf(' ') !== -1) {
- str = str.replace(' ', ' ');
- }
- if (str.charAt(0) == ' ') {
- str = str.substring(1);
- }
- if (str.charAt(str.length - 1) == ' ') {
- str = str.substring(0, str.length - 1);
- }
- return str;
-}
\ No newline at end of file
diff --git a/public/js/beathaven/pages.js b/public/js/beathaven/pages.js
deleted file mode 100644
index b6fdd5e..0000000
--- a/public/js/beathaven/pages.js
+++ /dev/null
@@ -1,97 +0,0 @@
-var Pages = {
-
- renderArtist: function(data){
- var artist_info = $('\
- \
-
\
-
\
-
\
-
'+ data.artist.name +'
\
-
\
- '+ data.artist.desc +'\
-
\
-
\
- ');
-
- var albums_info = $('');
- $.each(data.albums, function(i, album){
- if (album.year != null) {
- var album_info = $('\
- \
-
'+ album.name +' ('+ album.year +')
\
-
\
-
\
-
\
-
\
-
\
-
\
- \
- ');
-
- $.each(album.tracks.album, function(i, track){
- var track_info = $('\
- \
- +
\
- \
-
\
-
'+ (i+1) +'\
-
'+ track.name +'
\
-
'+ track.duration +'
\
-
\
- \
- ');
-
- $(album_info).find('.tracklist ul').append(track_info);
- });
-
- $(albums_info).append(album_info);
- }
- })
-
- $('.data-container').css('backgroundImage', 'none');
- $('.data-container .inner').html('').append(artist_info).append(albums_info);
-
- yaCounter7596904.hit(Ajax.getAnchor(), data.artist.name, Ajax.referer);
- Ajax.setTitle(data.artist.name);
- },
-
- renderSearch: function(data) {
- $('.data-container').css('background', 'url(/images/concrete_wall_2.png) 0 -30px repeat');
- $('.data-container .inner').html(data);
-
- $('.search-container')
- .css('marginLeft', ($('.data-container').width() - 425) / 2 + 'px')
- .css('marginTop', ($('.data-container').height() / 2 - 230) +'px')
- .height(($('.data-container').height() - $('#search_form').height()) / 2);
-
- setTimeout(function(){
- $('#search_field').bh_autocomplete({
- serviceUrl: '/artist/autocomplete', // Страница для обработки запросов автозаполнения
- minChars: 2, // Минимальная длина запроса для срабатывания автозаполнения
- delimiter: /(,|;)\s*/, // Разделитель для нескольких запросов, символ или регулярное выражение
- maxHeight: 400, // Максимальная высота списка подсказок, в пикселях
- width: 415, // Ширина списка
- zIndex: 9999, // z-index списка
- deferRequestBy: 500, // Задержка запроса (мсек)
- onSelect: Ajax.loadArtistData
- });
- $('#search_field').focus();
- }, 501)
-
- yaCounter7596904.hit(Ajax.getAnchor(), 'Artist Search', Ajax.referer);
- Ajax.setTitle('Artist Search');
- },
-
- renderSettings: function(data) {
- $('.data-container').css('background', 'none');
- $('.data-container .inner').html(data);
- yaCounter7596904.hit(Ajax.getAnchor(), 'Settings', Ajax.referer);
- Ajax.setTitle('Settings');
- $('.settings-container .tabs .tab').first().trigger('click');
- }
-}
\ No newline at end of file
diff --git a/public/js/beathaven/player.js b/public/js/beathaven/player.js
deleted file mode 100644
index 076926d..0000000
--- a/public/js/beathaven/player.js
+++ /dev/null
@@ -1,261 +0,0 @@
-var Player = {
-
- bar_width: 330,
- jp: null,
- scrobbled: false,
-
- initJplayer: function() {
- this.jp = $("#jplayer");
- this.jp.jPlayer({
- swfPath: "/js",
- supplied: "mp3",
- cssSelectorAncestor: "",
- cssSelector: {
- play: "#player .play",
- pause: "#player .pause",
- stop: "",
- videoPlay: "",
- seekBar: "",
- playBar: "",
- mute: "",
- unmute: "",
- volumeBar: "",
- volumeBarValue: "",
- currentTime: "",
- duration: ""
- }
- });
- this.jp.bind($.jPlayer.event.timeupdate, function(e){
- data = e.jPlayer.status;
- if (!Player.scrobbled && data.currentPercentAbsolute > 50) {
- var $obj = $('.playlist-tracks li.now');
- Player.scrobble($obj.attr('data-artist'), $obj.attr('data-album'), $obj.attr('data-track'));
- Player.scrobbled = true;
- }
- $('#player .progress .loaded').width(data.seekPercent * Player.bar_width / 100);
- $('#player .progress .played').width(data.currentPercentAbsolute * Player.bar_width / 100);
- });
- this.jp.bind($.jPlayer.event.ended, function(e){
- var next = Player.nextTrack();
- if (next === false) {
- $('#jplayer').jPlayer('clearMedia');
- $('#player .now-playing').html('Nothing left to lose play');
- $('#player .loaded, #player .played').width(0);
- $('.playlist-tracks li').removeClass('now');
- } else {
- Player.setTrack(next);
- }
- });
- },
-
- addTrack: function(artist, album, track, length, autoplay) {
- if (typeof autoplay === 'undefined') {
- autoplay = false;
- }
- var initial_count = $('.playlist-tracks li').length;
- $('.playlist-tracks').append('\
- \
- \
-
\
-
'+ artist +' — '+ track +'\
-
'+ length +'\
-
remove
\
-
\
- \
- ');
-
- $('#playlist').html($('.playlist-tracks')).scrollbar();
- $('.playlist-tracks').sortable();
-
- if (autoplay) {
- Player.setTrack($('.playlist-tracks li').last().attr('id').split('i')[1]);
- } else if (initial_count === 0 && !Player.hasTrack()) {
- Player.setTrack($('.playlist-tracks li').first().attr('id').split('i')[1]);
- }
- },
-
- setTrack: function(id) {
- var $obj = $('#i'+ id);
- var query = $obj.attr('data-artist') +' — '+ $obj.attr('data-track');
-
- $('#player .now-playing').html(query +'');
- $('.playlist-tracks li').removeClass('now');
- $obj.addClass('now');
- $('#player .loaded, #player .played').width(0);
-
- Vkontakte.loadTracksData($obj.attr('data-artist'), $obj.attr('data-track'), $obj.attr('data-length'), Player.playSource);
- Player.updateNowListening($obj.attr('data-artist'), $obj.attr('data-album'), $obj.attr('data-track'));
- },
-
- hasTrack: function() {
- if ($('#jplayer audio').length > 0) {
- return typeof $('#jplayer audio').attr('src') !== 'undefined';
- } else if ($('#jplayer audio').length > 0) {
- $('#jplayer').jPlayer('play');
- return true;
- }
- return false;
- },
-
- playSource: function(url) {
- $('#jplayer').jPlayer('setMedia', {mp3: url}).jPlayer('play');
- Player.scrobbled = false;
- },
-
- nextTrack: function(manual) {
- var manual = typeof manual !== 'undefined'
- var cnt = $('.playlist-tracks li').length;
- if (!Player.onShuffle()) { // Shuffle off
- if ($('.playlist-tracks .now').next().length == 0 && true) {
- if (Player.onRepeat() || manual) { // Last track and repeat is on
- return $('.playlist-tracks li').first().attr('id').split('i')[1];
- } else {
- return false;
- }
- } else {
- return $('.playlist-tracks .now').next().attr('id').split('i')[1];
- }
- } else if (cnt === 1) { // Single track in the playlist
- return $('.playlist-tracks li').first().attr('id').split('i')[1];
- } else { // Shuffle on
- while (1) {
- var rnd = Math.floor(Math.random() * (cnt + .999));
- var $li = $('.playlist-tracks li').eq(rnd);
- if ($li.length > 0 && !$li.hasClass('now')) {
- return $li.attr('id').split('i')[1];
- }
- }
- }
- },
-
- prevTrack: function() {
- var cnt = $('.playlist-tracks li').length;
- if (!Player.onShuffle()) { // Shuffle off
- if ($('.playlist-tracks .now').prev().length == 0 && true) { // First track in the playlist
- return $('.playlist-tracks li').last().attr('id').split('i')[1];
- } else {
- return $('.playlist-tracks .now').prev().attr('id').split('i')[1];
- }
- } else if (cnt === 1) { // Single track in the playlist
- return $('.playlist-tracks li').first().attr('id').split('i')[1];
- } else { // Shuffle on
- while (1) {
- var rnd = Math.floor(Math.random() * (cnt + .999));
- var $li = $('.playlist-tracks li').eq(rnd);
- if ($li.length > 0 && !$li.hasClass('now')) {
- return $li.attr('id').split('i')[1];
- }
- }
- }
- },
-
- onShuffle: function() {
- return $('#shuffle').hasClass('active');
- },
-
- onRepeat: function() {
- return $('#repeat').hasClass('active');
- },
-
- updateNowListening: function(artist, album, track) {
- if (Settings.user.lastfm_username) {
- Session.query('/lastfm/listening?r='+ Math.random(), {'artist': artist, 'album': album, 'track': track});
- }
- },
-
- scrobble: function(artist, album, track) {
- if (Settings.user.lastfm_username) {
- Session.query('/lastfm/scrobble?r='+ Math.random(), {'artist': artist, 'album': album, 'track': track});
- }
- }
-}
-
-$(function(){
- Player.initJplayer();
-});
-
-$('#player .controls .prev').live('click', function(){
- Player.setTrack(Player.prevTrack());
-});
-
-$('#player .controls .next').live('click', function(){
- Player.setTrack(Player.nextTrack(true));
-});
-
-$('.add-album').live('click', function() {
- var artist = $('.artist-info .name').html();
- var album = $(this).parent().parent().parent().find('h2.name').text().replace(/\s\([\d]{4}\)$/, '');
- $(this).parent().parent().parent().find('.tracklist li').each(function(i, item){
- var track_name = $(item).find('.trackname').html();
- var length = $(item).find('.length').html();
- Player.addTrack(artist, album, track_name, length);
- });
-})
-$('.add-track').live('click', function(){
- var artist = $('.artist-info .name').html();
- var album = $(this).parent().parent().parent().parent().find('h2.name').text().replace(/\s\([\d]{4}\)$/, '');
- var track_name = $(this).parent().find('.trackname').html();
- var length = $(this).parent().find('.length').html();
- Player.addTrack(artist, album, track_name, length);
-});
-
-$('#player .play').live('click', function(){
- if ($('.playlist-tracks li').length > 0 && !Player.hasTrack()) {
- Player.setTrack($('.playlist-tracks li').first().attr('id').split('i')[1]);
- }
-});
-
-$('.tracklist li').live('mouseover mouseout', function(e){
- if (e.type == 'mouseover') {
- $(this).find('.add-track').show();
- } else {
- $(this).find('.add-track').hide();
- }
-}).live('dblclick', function(e){
- var artist = $('.artist-info .name').html();
- var album = $(this).parent().parent().parent().find('h2.name').text().replace(/\s\([\d]{4}\)$/, '');
- var track_name = $(this).find('.trackname').html();
- var length = $(this).find('.length').html();
- Player.addTrack(artist, album, track_name, length, true);
-});
-
-$('.playlist-tracks li .fade, .playlist-tracks li .duration, .playlist-tracks li .remove').live('mouseover mouseout', function(e){
- if (e.type == 'mouseover') {
- $(this).parent().find('.duration').hide();
- $(this).parent().find('.remove').show();
- } else {
- $(this).parent().find('.remove').hide();
- $(this).parent().find('.duration').show();
- }
-});
-
-$('.playlist-tracks li .remove').live('click', function(){
- var $li = $(this).parent().parent();
- if ($li.hasClass('now')) {
- $('#jplayer').jPlayer('clearMedia');
- $('#player .now-playing').text('...');
- $('#player .loaded, #player .played').width(0);
- }
- $li.remove();
-});
-
-$('.playlist-tracks li').live('dblclick', function(){
- Player.setTrack($(this).attr('id').split('i')[1]);
-});
-
-$('#player .progress').live('click', function(e){
- $('#jplayer').jPlayer('playHead', Math.round((e.offsetX / Player.bar_width) * 100));
-});
-
-$('#repeat, #shuffle').live('click', function(){
- $(this).toggleClass('active');
-});
-
-$('#empty-playlist').live('click', function(){
- if (confirm('Are you sure?')) {
- $('.playlist-tracks li').remove();
- $('#jplayer').jPlayer('clearMedia');
- $('#player .now-playing').text('Add some music to playlist');
- $('#player .loaded, #player .played').width(0);
- }
-});
\ No newline at end of file
diff --git a/public/js/beathaven/search.js b/public/js/beathaven/search.js
deleted file mode 100644
index 47ac40b..0000000
--- a/public/js/beathaven/search.js
+++ /dev/null
@@ -1,71 +0,0 @@
-var Search = {
-
- pics: [],
-
- showSpinner: function() {
- $('.search-container input').attr('disabled', 'disabled').blur();
- $('.search-container img').show();
- $('.autocomplete-container').hide();
- $('.artist_loading.failed').hide();
- Search.hideSuggestions();
- },
-
- hideSpinner: function() {
- $('.search-container input').removeAttr('disabled');
- $('.search_field').focus();
- $('.search-container img').hide();
- },
-
- showSuggestions: function(values) {
- for (var i = 0; i < values.length; i++) {
- $('.suggestions ul').append('\
- \
- '+ values[i].name +'\
- '+ (values[i].desc ? '
'+ values[i].desc +'' : '') +'\
- \
- ');
- }
- $('.suggestions').show();
- },
-
- hideSuggestions: function() {
- $('.suggestions ul li').remove();
- $('.suggestions').hide();
- },
-
- showArtistPics: function(pics) {
- $('.artist_loading.ok, .artist_pics').show();
- for (var i = 0; i < pics.length; i++) {
- if (Search.pics.indexOf(pics[i]) === -1) {
- Search.pics.push(pics[i]);
- $('.artist_pics').append('\
- \
-
\
-
\
- ');
- }
- }
- },
-
- showError: function() {
- $('.artist_loading.ok, .artist_pics').hide();
- $('.artist_loading.failed').show();
- Search.pics = []
- }
-}
-
-$(function(){
- $('.search').live('click', function(){
- Ajax.loadSearchPage();
- return false;
- });
- $('#search_form').live('submit', function(){
- $('.autocomplete-container').remove();
- Ajax.loadArtistData($('#search_field').val());
- return false;
- });
- $('.suggestions a').live('click', function(){
- $('#search_field').val($(this).text());
- return false;
- });
-});
\ No newline at end of file
diff --git a/public/js/beathaven/session.js b/public/js/beathaven/session.js
deleted file mode 100644
index 1fb5d64..0000000
--- a/public/js/beathaven/session.js
+++ /dev/null
@@ -1,20 +0,0 @@
-var Session = {
-
- user_id: null,
- vk_params: {},
-
- query: function(url, params, callback) {
- var q_params = this.vk_params;
- for (attr in params) {
- q_params[attr] = params[attr];
- }
- $.post(url, q_params, callback);
- },
-
- setVkParams: function(params) {
- attrs = ['expire', 'mid', 'secret', 'sid', 'sig'];
- for (var i = 0; i < attrs.length; i++) {
- this.vk_params[attrs[i]] = params[attrs[i]];
- }
- }
-}
\ No newline at end of file
diff --git a/public/js/beathaven/settings.js b/public/js/beathaven/settings.js
deleted file mode 100644
index 892a159..0000000
--- a/public/js/beathaven/settings.js
+++ /dev/null
@@ -1,53 +0,0 @@
-var Settings = {
-
- user: null,
-
- getAccountInfo: function(callback) {
- Session.query('/user/update', {}, callback);
- },
-
- saveAccountInfo: function(params, callback) {
- Session.query('/user/update', params, callback);
- },
-
- loadFormData: function(form) {
- if (form == 'account') {
- $('.settings-container .form input[name$="username"]').val(Settings.user.name);
- $('.settings-container .form input[name$="email"]').val(Settings.user.email);
- } else if (form == 'lastfm') {
- if (Settings.user.lastfm_username) {
- $('.form-container input[name$="username"]').first().val(Settings.user.lastfm_username);
- }
- }
- }
-}
-
-$('.settings').live('click', function() {
- Ajax.loadSettingsPage();
-});
-
-$('.settings-container .tabs .tab').live('click', function(){
- if (!$(this).hasClass('active')) {
- $('.settings-container .tabs .tab').removeClass('active');
- $(this).addClass('active');
- $('.form-container').html($('.forms .'+ $(this).attr('data-fieldset')).html());
- Settings.loadFormData($(this).attr('data-fieldset'));
- }
-});
-
-$('.lastfm-connect').live('click', function(){
- window.open(Settings.user.lastfm_login_url);
-});
-
-$('.settings-container .form input').live('blur', function(){
- var active_tab = $('.settings-container .tabs .tab.active').attr('data-fieldset');
- if (active_tab == 'account') {
- params = {
- 'username': $('.settings-container .form input[name$="username"]').first().val(),
- 'email': $('.settings-container .form input[name$="email"]').first().val(),
- };
- Settings.saveAccountInfo(params, function(){
- $('#header-container .hello .greating').text('Hi there, '+ (params.username.length > 0 ? params.username : '%username%') +'!');
- });
- }
-})
\ No newline at end of file
diff --git a/public/js/beathaven/vkontakte.js b/public/js/beathaven/vkontakte.js
deleted file mode 100644
index 0ae899c..0000000
--- a/public/js/beathaven/vkontakte.js
+++ /dev/null
@@ -1,102 +0,0 @@
-var Vkontakte = {
-
- qr: [],
-
- authInfo: function(response) {
- if (typeof response != 'undefined' && response.session) {
- Session.setVkParams(response.session);
- $('#vk_login, .auth_notice').hide();
- $('#vk_logout').css('display', 'block');
- if ($('#search_field').length > 0) {
- $('#search_field').focus();
- }
- Session.query('/user/auth', {}, function(ar){
- if (ar.newbie) {
- VK.Api.call('getVariable', {key: 1281}, function(r) {
- Session.query('/user/update', {'name': r.response}, function(ar2) {
- Settings.user = ar2.user;
- $('#header-container .hello .greating').text('Hi there, '+ (Settings.user.name ? Settings.user.name : '%username%') +'!');
- });
- });
- } else {
- Settings.user = ar.user;
- }
- $('#header-container .hello .greating').text('Hi there, '+ (Settings.user.name ? Settings.user.name : '%username%') +'!');
- });
- } else {
- $('#vk_login, .auth_notice').css('display', 'block');
- $('#vk_logout').hide();
- }
- },
-
- loadTracksData: function(artist, track, duration, callback) {
- var track_prepared = track.replace(/\(.*\)/i, '').split('/')[0];
- var query = artist +' '+ track_prepared;
- if (typeof Vkontakte.qr[query] !== 'undefined') {
- callback(Vkontakte.qr[query]);
- } else {
- VK.Api.call('audio.search', {q: query}, function(r){
- var url = Vkontakte.matchPerfectResult(r.response, artist, track, duration);
- Vkontakte.qr[query] = url;
- callback(url);
- });
- }
- },
-
- matchPerfectResult: function(data, artist, track, duration) {
- var duration = duration.split(':');
- if (duration[1].charAt(0) === '0') {
- duration[1] = duration[1].substring(1);
- }
- duration = parseInt(duration[0]) * 60 + parseInt(duration[1]);
- var best_score = 0;
- var best_result = null;
- for (var i = 1; i < data.length; i++) {
- var score = 0;
- data[i].artist = data[i].artist.trim();
- data[i].title = data[i].title.trim();
- if (data[i].artist === artist) {
- score += 10;
- } else if (data[i].artist.split(artist).length === 2) {
- score += 5;
- } else if (data[i].title.split(artist).length === 2) {
- score += 4;
- }
- if (data[i].title === track) {
- score += 10;
- } else if (data[i].title.split(track).length === 2) {
- score += 5;
- }
- if (parseInt(data[i].duration) === duration) {
- score += 15;
- } else {
- var delta = Math.abs(parseInt(data[i].duration) - duration);
- if (delta < 10) {
- score += (10 - delta);
- }
- }
- if (score > best_score) {
- best_score = score;
- best_result = data[i];
- }
- if (score === 35) {
- return best_result.url;
- }
- }
- return best_result.url;
- }
-}
-
-$(function(){
- VK.init({
- apiId: (document.location.host == 'beathaven.org' ? 2335068 : 2383163),
- nameTransportPath: "/xd_receiver.html"
- });
- VK.Auth.getLoginStatus(Vkontakte.authInfo);
- $('#vk_login').click(function(){
- VK.Auth.login(Vkontakte.authInfo, 8);
- });
- $('#vk_logout').click(function(){
- VK.Auth.logout(Vkontakte.authInfo);
- });
-})
\ No newline at end of file
diff --git a/public/stylesheets/.gitkeep b/public/stylesheets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/public/css/_sass/_alpha_gradient.scss b/public/stylesheets/_sass/_alpha_gradient.scss
similarity index 100%
rename from public/css/_sass/_alpha_gradient.scss
rename to public/stylesheets/_sass/_alpha_gradient.scss
diff --git a/public/css/_sass/_opacity.scss b/public/stylesheets/_sass/_opacity.scss
similarity index 100%
rename from public/css/_sass/_opacity.scss
rename to public/stylesheets/_sass/_opacity.scss
diff --git a/public/css/_sass/_rounded.scss b/public/stylesheets/_sass/_rounded.scss
similarity index 100%
rename from public/css/_sass/_rounded.scss
rename to public/stylesheets/_sass/_rounded.scss
diff --git a/public/css/_sass/albums.scss b/public/stylesheets/_sass/albums.scss
similarity index 100%
rename from public/css/_sass/albums.scss
rename to public/stylesheets/_sass/albums.scss
diff --git a/public/css/_sass/common.scss b/public/stylesheets/_sass/common.scss
similarity index 100%
rename from public/css/_sass/common.scss
rename to public/stylesheets/_sass/common.scss
diff --git a/public/css/_sass/header.scss b/public/stylesheets/_sass/header.scss
similarity index 100%
rename from public/css/_sass/header.scss
rename to public/stylesheets/_sass/header.scss
diff --git a/public/css/_sass/misc.scss b/public/stylesheets/_sass/misc.scss
similarity index 100%
rename from public/css/_sass/misc.scss
rename to public/stylesheets/_sass/misc.scss
diff --git a/public/css/_sass/player.scss b/public/stylesheets/_sass/player.scss
similarity index 100%
rename from public/css/_sass/player.scss
rename to public/stylesheets/_sass/player.scss
diff --git a/public/css/_sass/search.scss b/public/stylesheets/_sass/search.scss
similarity index 100%
rename from public/css/_sass/search.scss
rename to public/stylesheets/_sass/search.scss
diff --git a/public/css/_sass/settings.scss b/public/stylesheets/_sass/settings.scss
similarity index 100%
rename from public/css/_sass/settings.scss
rename to public/stylesheets/_sass/settings.scss
diff --git a/public/css/albums.css b/public/stylesheets/albums.css
similarity index 100%
rename from public/css/albums.css
rename to public/stylesheets/albums.css
diff --git a/public/css/beathaven.css b/public/stylesheets/beathaven.css
similarity index 100%
rename from public/css/beathaven.css
rename to public/stylesheets/beathaven.css
diff --git a/public/css/common.css b/public/stylesheets/common.css
similarity index 100%
rename from public/css/common.css
rename to public/stylesheets/common.css
diff --git a/public/css/header.css b/public/stylesheets/header.css
similarity index 100%
rename from public/css/header.css
rename to public/stylesheets/header.css
diff --git a/public/css/misc.css b/public/stylesheets/misc.css
similarity index 100%
rename from public/css/misc.css
rename to public/stylesheets/misc.css
diff --git a/public/css/player.css b/public/stylesheets/player.css
similarity index 100%
rename from public/css/player.css
rename to public/stylesheets/player.css
diff --git a/public/css/search.css b/public/stylesheets/search.css
similarity index 100%
rename from public/css/search.css
rename to public/stylesheets/search.css
diff --git a/public/css/settings.css b/public/stylesheets/settings.css
similarity index 100%
rename from public/css/settings.css
rename to public/stylesheets/settings.css