Tracks import
This commit is contained in:
		
							parent
							
								
									b5f616a9d9
								
							
						
					
					
						commit
						43422624f1
					
				
							
								
								
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							@ -11,12 +11,13 @@ group :assets do
 | 
			
		||||
  gem "hogan_assets"
 | 
			
		||||
 | 
			
		||||
  gem "uglifier", ">= 1.0.3"
 | 
			
		||||
  gem "bourbon"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
gem "jquery-rails"
 | 
			
		||||
 | 
			
		||||
# To use Jbuilder templates for JSON
 | 
			
		||||
# gem "jbuilder"
 | 
			
		||||
gem "jbuilder"
 | 
			
		||||
 | 
			
		||||
gem "thin"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,10 @@ GEM
 | 
			
		||||
      i18n (~> 0.6)
 | 
			
		||||
      multi_json (~> 1.0)
 | 
			
		||||
    arel (3.0.2)
 | 
			
		||||
    blankslate (2.1.2.4)
 | 
			
		||||
    bootstrap-sass (2.0.4.0)
 | 
			
		||||
    bourbon (2.1.1)
 | 
			
		||||
      sass (>= 3.1)
 | 
			
		||||
    builder (3.0.0)
 | 
			
		||||
    capistrano (2.12.0)
 | 
			
		||||
      highline
 | 
			
		||||
@ -80,6 +83,9 @@ GEM
 | 
			
		||||
      multi_json (~> 1.0)
 | 
			
		||||
      multi_xml
 | 
			
		||||
    i18n (0.6.0)
 | 
			
		||||
    jbuilder (0.4.0)
 | 
			
		||||
      activesupport (>= 3.0.0)
 | 
			
		||||
      blankslate (>= 2.1.2.4)
 | 
			
		||||
    journey (1.0.4)
 | 
			
		||||
    jquery-rails (2.1.1)
 | 
			
		||||
      railties (>= 3.1.0, < 5.0)
 | 
			
		||||
@ -168,10 +174,12 @@ PLATFORMS
 | 
			
		||||
DEPENDENCIES
 | 
			
		||||
  beatparser!
 | 
			
		||||
  bootstrap-sass (~> 2.0.4.0)
 | 
			
		||||
  bourbon
 | 
			
		||||
  capistrano
 | 
			
		||||
  coffee-rails (~> 3.2.1)
 | 
			
		||||
  eco
 | 
			
		||||
  hogan_assets
 | 
			
		||||
  jbuilder
 | 
			
		||||
  jquery-rails
 | 
			
		||||
  pg
 | 
			
		||||
  rails (= 3.2.8)
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
//
 | 
			
		||||
//= require jquery
 | 
			
		||||
//= require jquery_ujs
 | 
			
		||||
//= require jquery.autocomplete
 | 
			
		||||
//= require mustache
 | 
			
		||||
//= require hogan
 | 
			
		||||
//= require underscore
 | 
			
		||||
@ -20,3 +21,4 @@
 | 
			
		||||
//= require backbone_datalink
 | 
			
		||||
//= require backbone/beat_haven
 | 
			
		||||
//= require_tree .
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								app/assets/javascripts/backbone/autocomplete.js.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/assets/javascripts/backbone/autocomplete.js.coffee
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
$ ->
 | 
			
		||||
  $(".navbar-search input").focus ->
 | 
			
		||||
    $(this).animate(width: 249)
 | 
			
		||||
    $(".player").animate(width: 408)
 | 
			
		||||
  $(".navbar-search input").blur ->
 | 
			
		||||
    $(this).animate(width: 99)
 | 
			
		||||
    $(".player").animate(width: 558)
 | 
			
		||||
 | 
			
		||||
  window.desired = $(".navbar-search input").autocomplete
 | 
			
		||||
    serviceUrl: "/api/search/complete"
 | 
			
		||||
    onSelect: (selected) ->
 | 
			
		||||
      Backbone.history.navigate("/search/"+selected.replace(/\s/g, "+"), true)
 | 
			
		||||
@ -9,10 +9,24 @@ window.BeatHaven =
 | 
			
		||||
  Collections: {}
 | 
			
		||||
  Routers: {}
 | 
			
		||||
  Views: {}
 | 
			
		||||
  player: null
 | 
			
		||||
 | 
			
		||||
  init: ->
 | 
			
		||||
    new BeatHaven.Routers.Artist()
 | 
			
		||||
    Backbone.history.start();
 | 
			
		||||
    new BeatHaven.Routers.Album()
 | 
			
		||||
    new BeatHaven.Routers.Search()
 | 
			
		||||
    @player = new BeatHaven.Models.Player()
 | 
			
		||||
 | 
			
		||||
    Backbone.history.start(pushState: true);
 | 
			
		||||
    $("a").live "click", (e) ->
 | 
			
		||||
      if $(this).attr("href").substr(0, 1) == "/"
 | 
			
		||||
        e.preventDefault()
 | 
			
		||||
        Backbone.history.navigate($(this).attr("href"), true)
 | 
			
		||||
        return false
 | 
			
		||||
      else
 | 
			
		||||
        alert "Window close attempt!"
 | 
			
		||||
        return false
 | 
			
		||||
      true
 | 
			
		||||
 | 
			
		||||
$ ->
 | 
			
		||||
  BeatHaven.init()
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
class BeatHaven.Collections.Tracklist extends Backbone.Collection
 | 
			
		||||
							
								
								
									
										2
									
								
								app/assets/javascripts/backbone/models/album.js.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/assets/javascripts/backbone/models/album.js.coffee
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
class BeatHaven.Models.Album extends Backbone.Model
 | 
			
		||||
  urlRoot: "/api/albums"
 | 
			
		||||
							
								
								
									
										11
									
								
								app/assets/javascripts/backbone/models/player.js.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/assets/javascripts/backbone/models/player.js.coffee
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
class BeatHaven.Models.Player extends Backbone.Model
 | 
			
		||||
  playlist_on: false
 | 
			
		||||
  playlist: null
 | 
			
		||||
  tracks: null
 | 
			
		||||
 | 
			
		||||
  initialize: ->
 | 
			
		||||
    @playlist = new BeatHaven.Collections.Tracklist()
 | 
			
		||||
    @tracks = new BeatHaven.Collections.Tracklist()
 | 
			
		||||
 | 
			
		||||
  update_title: (params) ->
 | 
			
		||||
    $(".player .progress-bar .title").html("#{params.artists.join(', ')} — #{params.track}")
 | 
			
		||||
							
								
								
									
										7
									
								
								app/assets/javascripts/backbone/models/track.js.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/assets/javascripts/backbone/models/track.js.coffee
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
class BeatHaven.Models.Track extends Backbone.Model
 | 
			
		||||
 | 
			
		||||
  play: ->
 | 
			
		||||
    BeatHaven.player.update_title(
 | 
			
		||||
      artists: @.get("artists")
 | 
			
		||||
      track: @.get("title")
 | 
			
		||||
    )
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
class BeatHaven.Routers.Album extends Backbone.Router
 | 
			
		||||
  routes:
 | 
			
		||||
    "album/:id": "show"
 | 
			
		||||
 | 
			
		||||
  show: (id) ->
 | 
			
		||||
    album = new BeatHaven.Models.Album(id: id)
 | 
			
		||||
    album.fetch()
 | 
			
		||||
    view = new BeatHaven.Views.AlbumShow(model: album)
 | 
			
		||||
    $("#main").html(view.render().el)
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
class BeatHaven.Routers.Search extends Backbone.Router
 | 
			
		||||
  routes:
 | 
			
		||||
    "search/:query": "search"
 | 
			
		||||
 | 
			
		||||
  search: (query) ->
 | 
			
		||||
    $(".navbar-search .search-query").attr("disabled", "disabled").blur()
 | 
			
		||||
    $.ajax(
 | 
			
		||||
      url: "/api/search/wtfis?q=#{query}"
 | 
			
		||||
      success: (data) ->
 | 
			
		||||
        if data.found?
 | 
			
		||||
          Backbone.history.navigate(data.found, true)
 | 
			
		||||
          $(".navbar-search .search-query").val("").removeAttr("disabled")
 | 
			
		||||
        else
 | 
			
		||||
          alert "Not found! :("
 | 
			
		||||
          $(".navbar-search .search-query").removeAttr("disabled").focus()
 | 
			
		||||
    )
 | 
			
		||||
@ -0,0 +1,28 @@
 | 
			
		||||
<div class="artist-page">
 | 
			
		||||
  <h1 class="title">{{album_title}} ({{album_year}})</h1>
 | 
			
		||||
  <div class="album">
 | 
			
		||||
    <div class="pic">
 | 
			
		||||
      <img src="{{album_pic}}" alt="{{album_title}}"><br>
 | 
			
		||||
      <a href="" class="btn btn-bh-blue album-add"><i class="icon-plus icon-white"></i> Add to playlist</a>
 | 
			
		||||
      <a href="" class="btn btn-bh-green album-play"><i class="icon-play icon-white"></i> Play</a>
 | 
			
		||||
    </div>
 | 
			
		||||
    <ul class="tracks">
 | 
			
		||||
    {{#album_tracks}}
 | 
			
		||||
      <li data-id="{{track_id}}">
 | 
			
		||||
        <a href="" class="btn btn-round track-play"><i class="icon-play"></i></a>
 | 
			
		||||
        <div class="title">
 | 
			
		||||
          <div class="inner">
 | 
			
		||||
            <a href="{{track_url}}" class="track-link">{{track_title}}</a>
 | 
			
		||||
            <span class="delimiter">—</span>
 | 
			
		||||
            <ul class="artists">
 | 
			
		||||
            {{#track_artists}}<li><a href="{{artist_url}}" class="artist-page">{{artist_title}}</a></li>{{/track_artists}}
 | 
			
		||||
            </ul>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <span class="length">{{track_length}}</span>
 | 
			
		||||
        <a href="" class="btn btn-round track-add"><i class="icon-plus"></i></a>
 | 
			
		||||
      </li>
 | 
			
		||||
    {{/album_tracks}}
 | 
			
		||||
    </ul>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,39 @@
 | 
			
		||||
<div class="artist-page">
 | 
			
		||||
  <h1>{{artist_title}}</h1>
 | 
			
		||||
  <div class="artist-info">
 | 
			
		||||
    <div class="pic">
 | 
			
		||||
      <img src="{{artist_pic}}" alt="{{artist_title}}">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="bio">{{& artist_bio}}</div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="albums">
 | 
			
		||||
  {{#artist_albums}}
 | 
			
		||||
    <div class="album">
 | 
			
		||||
      <div class="pic">
 | 
			
		||||
        <img src="{{album_pic}}" alt="{{album_title}}"><br>
 | 
			
		||||
        <div class="bh-underlay-blue">
 | 
			
		||||
          <a href="" class="btn btn-bh-dark album-add"><i class="icon-plus icon-white"></i> Add to playlist</a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="bh-underlay-green">
 | 
			
		||||
          <a href="" class="btn btn-bh-dark album-play"><i class="icon-play icon-white"></i> Play</a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <h2 class="title">{{album_title}} ({{album_year}})</h2>
 | 
			
		||||
      <ul class="tracks">
 | 
			
		||||
      {{#album_tracks}}
 | 
			
		||||
        <li data-id="{{track_id}}">
 | 
			
		||||
          <a href="" class="btn btn-round track-play"><i class="icon-play"></i></a>
 | 
			
		||||
          <div class="title">
 | 
			
		||||
            <div class="inner">
 | 
			
		||||
              <a href="{{track_url}}" class="track-link">{{track_title}}</a>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <span class="length">{{track_length}}</span>
 | 
			
		||||
          <a href="" class="btn btn-round track-add"><i class="icon-plus"></i></a>
 | 
			
		||||
        </li>
 | 
			
		||||
      {{/album_tracks}}
 | 
			
		||||
      </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
  {{/artist_albums}}
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@ -1,39 +0,0 @@
 | 
			
		||||
<div class="artist-page">
 | 
			
		||||
  <h1>{{name}}</h1>
 | 
			
		||||
  <div class="artist-info">
 | 
			
		||||
    <div class="pic">
 | 
			
		||||
      <img src="{{pic}}" alt="{{name}}">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="bio">{{bio}}</div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="albums">
 | 
			
		||||
  {{#albums}}
 | 
			
		||||
    <div class="album">
 | 
			
		||||
      <div class="pic">
 | 
			
		||||
        <img src="{{pic_safe}}" alt="{{title}}"><br>
 | 
			
		||||
        <a href="" class="btn btn-success play-all"><i class="icon-plus icon-white"></i> Add to playlist</a>
 | 
			
		||||
        <a href="" class="btn btn-info play-all"><i class="icon-play icon-white"></i> Play</a>
 | 
			
		||||
      </div>
 | 
			
		||||
      <h2 class="title">{{title}} ({{year}})</h2>
 | 
			
		||||
      <div class="tracks">
 | 
			
		||||
        <table class="table">
 | 
			
		||||
          <tbody>
 | 
			
		||||
          {{#tracks}}
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td class="title">
 | 
			
		||||
                <a href="" class="btn btn-round track-play"><i class="icon-play"></i></a>
 | 
			
		||||
                <a href="" class="track-link">{{title}}</a>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="length">
 | 
			
		||||
                <span class="length">{{length}}</span>
 | 
			
		||||
                <a href="" class="btn btn-round track-add"><i class="icon-plus"></i></a>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
          {{/tracks}}
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  {{/albums}}
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
class BeatHaven.Views.AlbumShow extends Backbone.View
 | 
			
		||||
  template: HoganTemplates["backbone/templates/album/show"]
 | 
			
		||||
 | 
			
		||||
  initialize: ->
 | 
			
		||||
    @model.on("change", @render, this)
 | 
			
		||||
 | 
			
		||||
  render: ->
 | 
			
		||||
    if typeof @model.get("album_tracks") != "undefined"
 | 
			
		||||
      for track_info in @model.get("album_tracks")
 | 
			
		||||
        track = new BeatHaven.Models.Track(track_info.meta)
 | 
			
		||||
        BeatHaven.player.tracks.push(track)
 | 
			
		||||
      $(@el).html(@template.render(@model.toJSON()))
 | 
			
		||||
    this
 | 
			
		||||
@ -1,10 +1,14 @@
 | 
			
		||||
class BeatHaven.Views.ArtistShow extends Backbone.View
 | 
			
		||||
  template: HoganTemplates["backbone/templates/artists/show"]
 | 
			
		||||
  template: HoganTemplates["backbone/templates/artist/show"]
 | 
			
		||||
 | 
			
		||||
  initialize: ->
 | 
			
		||||
    @model.on("change", @render, this)
 | 
			
		||||
 | 
			
		||||
  render: ->
 | 
			
		||||
    return this if typeof @model.attributes.id is "string"
 | 
			
		||||
    $(@el).html(@template.render(@model.toJSON()))
 | 
			
		||||
    if typeof @model.get("artist_bio") != "undefined"
 | 
			
		||||
      for album_info in @model.get("artist_albums")
 | 
			
		||||
        for track_info in album_info.album_tracks
 | 
			
		||||
          track = new BeatHaven.Models.Track(track_info.meta)
 | 
			
		||||
          BeatHaven.player.tracks.push(track)
 | 
			
		||||
      $(@el).html(@template.render(@model.toJSON()))
 | 
			
		||||
    this
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										126
									
								
								app/assets/stylesheets/album-track.css.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								app/assets/stylesheets/album-track.css.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,126 @@
 | 
			
		||||
.artist-page .album .tracks {
 | 
			
		||||
  margin-left: 265px;
 | 
			
		||||
 | 
			
		||||
  & > li {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    display: block;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 38px;
 | 
			
		||||
    list-style: none;
 | 
			
		||||
    font-family: "Source Sans Pro", $helvetica;
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
    line-height: 28px;
 | 
			
		||||
    text-shadow: 0 0 1px rgba(0, 0, 0, .3);
 | 
			
		||||
 | 
			
		||||
    &:after {
 | 
			
		||||
      content: "";
 | 
			
		||||
      display: block;
 | 
			
		||||
      width: 627px;
 | 
			
		||||
      float: right;
 | 
			
		||||
      border-bottom: #e0e0e0 1px solid;
 | 
			
		||||
      margin: 0 5px 0 0;
 | 
			
		||||
    }
 | 
			
		||||
    &:first-child:after, &:hover:after {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .btn-round {
 | 
			
		||||
      width: 10px;
 | 
			
		||||
      height: 22px;
 | 
			
		||||
      border-radius: 50px;
 | 
			
		||||
 | 
			
		||||
      i {
 | 
			
		||||
        margin: 3px 0 0 -1px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .track-play {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 3px;
 | 
			
		||||
      left: 5px;
 | 
			
		||||
 | 
			
		||||
      i {
 | 
			
		||||
        opacity: .3;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Track title
 | 
			
		||||
    .title {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 5px;
 | 
			
		||||
      left: 42px;
 | 
			
		||||
      height: 28px;
 | 
			
		||||
      max-width: 580px;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
 | 
			
		||||
      .inner {
 | 
			
		||||
        width: 1000px;
 | 
			
		||||
 | 
			
		||||
        .track-link {
 | 
			
		||||
          color: #303030;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .artists {
 | 
			
		||||
          @include inline-block;
 | 
			
		||||
          margin: 0;
 | 
			
		||||
          padding: 0;
 | 
			
		||||
 | 
			
		||||
          li {
 | 
			
		||||
            @include inline-block;
 | 
			
		||||
            margin: 0;
 | 
			
		||||
            padding: 0;
 | 
			
		||||
            color: #808080;
 | 
			
		||||
 | 
			
		||||
            .artist-page {
 | 
			
		||||
              color: #808080;
 | 
			
		||||
            }
 | 
			
		||||
            .delimiter {
 | 
			
		||||
              color: #808080;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &:before {
 | 
			
		||||
              content: ", ";
 | 
			
		||||
            }
 | 
			
		||||
            &:first-child:before {
 | 
			
		||||
              content: "";
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .length {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 5px;
 | 
			
		||||
      right: 5px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .track-add {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 3px;
 | 
			
		||||
      right: 5px;
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // li:hover
 | 
			
		||||
    &:hover{
 | 
			
		||||
      border: {
 | 
			
		||||
        width: 0px;
 | 
			
		||||
        style: solid;
 | 
			
		||||
        color: transparent;
 | 
			
		||||
        radius: 4px;
 | 
			
		||||
      }
 | 
			
		||||
      background-color: rgba(200, 200, 200, .3);
 | 
			
		||||
 | 
			
		||||
      .track-play i {
 | 
			
		||||
        opacity: 1;
 | 
			
		||||
      }
 | 
			
		||||
      .track-add {
 | 
			
		||||
        display: block;
 | 
			
		||||
      }
 | 
			
		||||
      .length {
 | 
			
		||||
        display: none;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1,27 +1,28 @@
 | 
			
		||||
.artist-page {
 | 
			
		||||
  h1 {
 | 
			
		||||
    font-family: Lobster, Georgia, serif;
 | 
			
		||||
    font-family: "Lobster Two", Georgia, serif;
 | 
			
		||||
    font-weight: 700;
 | 
			
		||||
    font-size: 42px;
 | 
			
		||||
    line-height: 64px;
 | 
			
		||||
    letter-spacing: 1px;
 | 
			
		||||
    text-shadow: 1px 1px 0 rgba(0, 0, 0, .2);
 | 
			
		||||
    text-shadow: 0 0 1px rgba(0, 0, 0, .3);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .artist-info {
 | 
			
		||||
    min-height: 250px;
 | 
			
		||||
    margin-bottom: 50px;
 | 
			
		||||
    margin-bottom: 20px;
 | 
			
		||||
 | 
			
		||||
    .pic {
 | 
			
		||||
      float: left;
 | 
			
		||||
 | 
			
		||||
      img {
 | 
			
		||||
        width: 250px;
 | 
			
		||||
        height: 250px;
 | 
			
		||||
        margin-top: 5px;
 | 
			
		||||
        border-size: 1px;
 | 
			
		||||
        border-style: solid;
 | 
			
		||||
        border-color: rgba(255, 255, 255, .2);
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
        box-shadow: 1px 1px 5px rgba(0, 0, 0, .2);
 | 
			
		||||
        @include box-shadow(0 0 3px 1px hsla(0, 0%, 0%, 0.3));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    .bio {
 | 
			
		||||
@ -34,13 +35,14 @@
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .album {
 | 
			
		||||
    min-height: 250px;
 | 
			
		||||
    min-height: 280px;
 | 
			
		||||
    margin-bottom: 30px;
 | 
			
		||||
 | 
			
		||||
    h2 {
 | 
			
		||||
      font-family: Lobster, Georgia, Serif;
 | 
			
		||||
      font-family: "Lobster Two", Georgia, Serif;
 | 
			
		||||
      font-weight: 700;
 | 
			
		||||
      font-size: 26px;
 | 
			
		||||
      text-shadow: 1px 1px 0 rgba(0, 0, 0, .2);
 | 
			
		||||
      text-shadow: 0 0 1px rgba(0, 0, 0, .3);
 | 
			
		||||
    }
 | 
			
		||||
    .pic {
 | 
			
		||||
      float: left;
 | 
			
		||||
@ -51,82 +53,18 @@
 | 
			
		||||
        background-color: #a0a0a0;
 | 
			
		||||
        width: 250px;
 | 
			
		||||
        height: 250px;
 | 
			
		||||
        margin-top: 5px;
 | 
			
		||||
        border-size: 1px;
 | 
			
		||||
        border-style: solid;
 | 
			
		||||
        border-color: rgba(255, 255, 255, .2);
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
        box-shadow: 1px 1px 5px rgba(0, 0, 0, .2);
 | 
			
		||||
        @include box-shadow(0 0 3px 1px hsla(0, 0%, 0%, 0.3));
 | 
			
		||||
        margin-bottom: 10px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    .title {
 | 
			
		||||
    & > .title {
 | 
			
		||||
      margin-left: 270px;
 | 
			
		||||
      margin-bottom: 10px;
 | 
			
		||||
    }
 | 
			
		||||
    .tracks {
 | 
			
		||||
      margin-left: 270px;
 | 
			
		||||
 | 
			
		||||
      tr:hover {
 | 
			
		||||
        td {
 | 
			
		||||
          background-color: rgba(200, 200, 200, .3);
 | 
			
		||||
 | 
			
		||||
          .track-play i {
 | 
			
		||||
            opacity: 1;
 | 
			
		||||
          }
 | 
			
		||||
          &.length {
 | 
			
		||||
            .track-add {
 | 
			
		||||
              display: block;
 | 
			
		||||
            }
 | 
			
		||||
            .length {
 | 
			
		||||
              display: none;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      td {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        font-family: "Source Sans Pro", Helvetica, sans-serif;
 | 
			
		||||
        font-size: 20px;
 | 
			
		||||
        line-height: 28px;
 | 
			
		||||
        text-shadow: 1px 1px 0 rgba(0, 0, 0, .1);
 | 
			
		||||
 | 
			
		||||
        .btn-round {
 | 
			
		||||
          width: 10px;
 | 
			
		||||
          height: 22px;
 | 
			
		||||
          border-radius: 50px;
 | 
			
		||||
 | 
			
		||||
          i {
 | 
			
		||||
            margin: 3px 0 0 -1px;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      td.title {
 | 
			
		||||
        padding-left: 40px;
 | 
			
		||||
        .track-play {
 | 
			
		||||
          position: absolute;
 | 
			
		||||
          margin: -2px 0 0 -40px;
 | 
			
		||||
 | 
			
		||||
          i {
 | 
			
		||||
            opacity: .3;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        .track-link {
 | 
			
		||||
          color: #303030;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      td.length {
 | 
			
		||||
        text-align: right;
 | 
			
		||||
 | 
			
		||||
        .track-add {
 | 
			
		||||
          position: absolute;
 | 
			
		||||
          right: 8px;
 | 
			
		||||
          margin: -2px 0 0 0;
 | 
			
		||||
          display: none;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								app/assets/stylesheets/autocomplete.css.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/assets/stylesheets/autocomplete.css.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
.autocomplete-w1 {
 | 
			
		||||
  margin: 10px 0 0 0;
 | 
			
		||||
  width: 268px;
 | 
			
		||||
  background: {
 | 
			
		||||
    color: #f0f0f0;
 | 
			
		||||
    color: rgba(240, 243, 246, .95);
 | 
			
		||||
  }
 | 
			
		||||
  @include box-shadow(5px 14px 5px rgba(0, 0, 0, .15));
 | 
			
		||||
 | 
			
		||||
  .autocomplete {
 | 
			
		||||
    div {
 | 
			
		||||
      width: 258px;
 | 
			
		||||
      padding: 5px 5px;
 | 
			
		||||
      border: {
 | 
			
		||||
        color: transparent;
 | 
			
		||||
        style: solid;
 | 
			
		||||
        width: 1px;
 | 
			
		||||
      }
 | 
			
		||||
      background: {
 | 
			
		||||
        color: #f0f0f0;
 | 
			
		||||
        color: rgba(240, 243, 246, .95);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:last-child {
 | 
			
		||||
        border-bottom: {
 | 
			
		||||
          left-radius: 5px;
 | 
			
		||||
          right-radius: 5px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &.selected {
 | 
			
		||||
        color: #ffffff;
 | 
			
		||||
        background-color: #49AFCD;
 | 
			
		||||
        background-color: rgba(73, 175, 205, .95);
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								app/assets/stylesheets/buttons.css.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/assets/stylesheets/buttons.css.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
.bh-underlay-blue, .bh-underlay-green {
 | 
			
		||||
  @include inline-block;
 | 
			
		||||
  border-color: transparent;
 | 
			
		||||
  @include border-radius(4px);
 | 
			
		||||
}
 | 
			
		||||
.bh-underlay-blue {
 | 
			
		||||
  background-color: #126be8;
 | 
			
		||||
  background: url($light_blue_noise);
 | 
			
		||||
}
 | 
			
		||||
.bh-underlay-green {
 | 
			
		||||
  background-color: #17ed25;
 | 
			
		||||
  background: url($light_green_noise);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-bh-dark, .btn-bh-light {
 | 
			
		||||
  background-position: 0 0 !important;
 | 
			
		||||
  color: #fafafa;
 | 
			
		||||
  text-shadow: 0 1px 1px rgba(0, 0, 0, .35);
 | 
			
		||||
  &:hover {
 | 
			
		||||
    color: #ffffff;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.btn-bh-dark {
 | 
			
		||||
  @include buttonBackground(rgba(0, 0, 0, .05), rgba(0, 0, 0, .15));
 | 
			
		||||
}
 | 
			
		||||
.btn-bh-light {
 | 
			
		||||
  @include buttonBackground(rgba(255, 255, 255, .05), rgba(255, 255, 255, .15));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								app/assets/stylesheets/noise.css.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/assets/stylesheets/noise.css.scss
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										51
									
								
								app/assets/stylesheets/player.css.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/assets/stylesheets/player.css.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
.player {
 | 
			
		||||
  width: 558px;
 | 
			
		||||
 | 
			
		||||
  .controls {
 | 
			
		||||
    float: left;
 | 
			
		||||
    margin: 3px 0 0 -103px;
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      margin: 5px 6px 0 -9px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  .progress-bar {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 26px;
 | 
			
		||||
    margin: 8px 0 0;
 | 
			
		||||
    background-color: rgba(0, 0, 0, .3);
 | 
			
		||||
    border: {
 | 
			
		||||
      style: solid;
 | 
			
		||||
      width: 1px;
 | 
			
		||||
      color: rgba(0, 0, 0, .3);
 | 
			
		||||
      radius: 4px;
 | 
			
		||||
    }
 | 
			
		||||
    @include box-shadow(inset 0 0 1px rgba(0, 0, 0, .2));
 | 
			
		||||
 | 
			
		||||
    .bar {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      height: 26px;
 | 
			
		||||
      background-color: rgba(255, 255, 255, .1);
 | 
			
		||||
      @include box-shadow(inset 0 0 2px rgba(255, 255, 255, .2));
 | 
			
		||||
      @include border-radius(4px 0px 0px 4px);
 | 
			
		||||
    }
 | 
			
		||||
    .title {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      left: 8px;
 | 
			
		||||
      font: {
 | 
			
		||||
        family: "Source Sans Pro", $helvetica;
 | 
			
		||||
        size: 14px;
 | 
			
		||||
      }
 | 
			
		||||
      line-height: 24px;
 | 
			
		||||
      color: #ffffff;
 | 
			
		||||
      text-shadow: 1px 1px 0 rgba(80, 80, 80, .4);
 | 
			
		||||
    }
 | 
			
		||||
    .move-it {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 26px;
 | 
			
		||||
      z-index: 9999;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -4,5 +4,12 @@ module Api
 | 
			
		||||
      album = Album.find(params[:id])
 | 
			
		||||
      redirect_to album.load_pic
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def show
 | 
			
		||||
      album = Album.find(params[:id])
 | 
			
		||||
      return render json: { fail: true } if album.nil?
 | 
			
		||||
 | 
			
		||||
      render json: album.dump_json
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								app/controllers/api/search_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/controllers/api/search_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
module Api
 | 
			
		||||
  class SearchController < ApplicationController
 | 
			
		||||
    def complete
 | 
			
		||||
      return render json: { suggestions: [] } if params[:query].to_s.length == 0
 | 
			
		||||
 | 
			
		||||
      suggestions = (Robbie::Autocomplete.complete(params[:query].to_s) || [])
 | 
			
		||||
      render json: {
 | 
			
		||||
        query: params[:query],
 | 
			
		||||
        suggestions: suggestions
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def wtfis
 | 
			
		||||
      result = (Robbie::Autocomplete.search(params[:q].to_s) || []).first
 | 
			
		||||
      unless result.nil?
 | 
			
		||||
        if result.instance_of? Robbie::Artist
 | 
			
		||||
          artist = Artist.find_or_create_by_rovi_id(result.id)
 | 
			
		||||
          puts artist.inspect
 | 
			
		||||
          unless artist.name?
 | 
			
		||||
            artist.import
 | 
			
		||||
          end
 | 
			
		||||
          return render json: { found: "/artist/#{result.name.gsub(" ", "+")}" }
 | 
			
		||||
        elsif result.instance_of? Robbie::Album
 | 
			
		||||
          album = Album.find_or_create_by_rovi_id(result.id)
 | 
			
		||||
          unless album.title?
 | 
			
		||||
            album.import
 | 
			
		||||
          end
 | 
			
		||||
          return render json: { found: "/album/#{album.id}" }
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      render json: { found: nil }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -2,7 +2,18 @@ class Album < ActiveRecord::Base
 | 
			
		||||
  belongs_to :artist
 | 
			
		||||
  has_many :tracks
 | 
			
		||||
 | 
			
		||||
  attr_accessible :artist_id, :pic, :rovi_id, :title, :year
 | 
			
		||||
  scope :shown, lambda {
 | 
			
		||||
    self
 | 
			
		||||
      .where('"albums"."year" > ?', 0)
 | 
			
		||||
      .where(is_hidden: false)
 | 
			
		||||
      .joins(:tracks)
 | 
			
		||||
      .group('"albums"."id"')
 | 
			
		||||
      .having('count("tracks"."id") > ?', 0)
 | 
			
		||||
      .order('"albums"."year" ASC')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  attr_accessible :artist_id, :pic, :rovi_id, :title, :year, :is_hidden
 | 
			
		||||
  VA = "Various Artists"
 | 
			
		||||
 | 
			
		||||
  def pic_safe
 | 
			
		||||
    unless pic.nil?
 | 
			
		||||
@ -13,7 +24,7 @@ class Album < ActiveRecord::Base
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def load_pic
 | 
			
		||||
    info = BeatParser::Sources::Lastfm.album_info(artist.name, title)
 | 
			
		||||
    info = BeatParser::Sources::Lastfm.album_info((artist.nil? ? VA : artist.name), title)
 | 
			
		||||
    unless info[:pic].nil?
 | 
			
		||||
      update_attributes(pic: info[:pic])
 | 
			
		||||
      info[:pic]
 | 
			
		||||
@ -21,4 +32,65 @@ class Album < ActiveRecord::Base
 | 
			
		||||
      "/assets/images/album-dummy.png"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def dump_json
 | 
			
		||||
    Jbuilder.encode do |j|
 | 
			
		||||
      j.album_title title
 | 
			
		||||
      j.album_year year
 | 
			
		||||
      j.album_pic pic_safe
 | 
			
		||||
      j.album_tracks tracks.to_a do |j, track|
 | 
			
		||||
        j.track_id track.id
 | 
			
		||||
        j.track_title track.title
 | 
			
		||||
        j.track_duration track.duration
 | 
			
		||||
        j.track_disc track.disc_id
 | 
			
		||||
        j.track_position track.position
 | 
			
		||||
        j.meta do |j|
 | 
			
		||||
          j.id track.id
 | 
			
		||||
          j.title track.title
 | 
			
		||||
          j.duration track.duration
 | 
			
		||||
          j.length track.length
 | 
			
		||||
          j.artists track.artists.map(&:name)
 | 
			
		||||
          j.album title
 | 
			
		||||
          j.album_pic pic_safe
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def import
 | 
			
		||||
    return unless rovi_id?
 | 
			
		||||
 | 
			
		||||
    Album.import(Robbie::Album.find(rovi_id))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
    def import(rovi_album)
 | 
			
		||||
      data = BeatParser::Aggregator.new.album(rovi_album.id)
 | 
			
		||||
      album = Album.find_or_create_by_rovi_id(data[:id])
 | 
			
		||||
      album.update_attributes(
 | 
			
		||||
        title: data[:title],
 | 
			
		||||
        year: data[:year].to_i
 | 
			
		||||
      )
 | 
			
		||||
      data[:tracks].each do |track_meta|
 | 
			
		||||
        track = Track.find_or_create_by_rovi_id(track_meta[:id])
 | 
			
		||||
        track.update_attributes(
 | 
			
		||||
          album_id: album.id,
 | 
			
		||||
          disc_id: track_meta[:disc_id],
 | 
			
		||||
          position: track_meta[:position],
 | 
			
		||||
          title: track_meta[:title],
 | 
			
		||||
          duration: track_meta[:duration]
 | 
			
		||||
        )
 | 
			
		||||
        track_meta[:artists].each do |performer|
 | 
			
		||||
          performer_artist = Artist.find_or_create_by_rovi_id(performer[:id])
 | 
			
		||||
          performer_artist.update_attributes(
 | 
			
		||||
            name: performer[:name]
 | 
			
		||||
          )
 | 
			
		||||
          Performer.find_or_create_by_artist_id_and_track_id(performer_artist.id, track.id)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
      album
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -11,21 +11,39 @@ class Artist < ActiveRecord::Base
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def dump_json
 | 
			
		||||
    serialized = to_json(
 | 
			
		||||
      include: {
 | 
			
		||||
        albums: {
 | 
			
		||||
          include: {
 | 
			
		||||
            tracks: {
 | 
			
		||||
              methods: [:length],
 | 
			
		||||
              except: [:created_at, :updated_at, :rovi_id, :album_id]
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          except: [:created_at, :updated_at, :rovi_id, :pic],
 | 
			
		||||
          methods: [:pic_safe]
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      except: [:created_at, :updated_at, :rovi_id],
 | 
			
		||||
    )
 | 
			
		||||
    Jbuilder.encode do |j|
 | 
			
		||||
      j.artist_title name
 | 
			
		||||
      j.artist_pic pic
 | 
			
		||||
      j.artist_bio bio
 | 
			
		||||
      j.artist_loaded loaded?
 | 
			
		||||
      j.artist_albums albums.shown.to_a do |j, album|
 | 
			
		||||
        j.album_title album.title
 | 
			
		||||
        j.album_year album.year
 | 
			
		||||
        j.album_pic album.pic_safe
 | 
			
		||||
        j.album_tracks album.tracks.to_a do |j, track|
 | 
			
		||||
          j.track_id track.id
 | 
			
		||||
          j.track_title track.title
 | 
			
		||||
          j.track_duration track.duration
 | 
			
		||||
          j.track_disc track.disc_id
 | 
			
		||||
          j.track_position track.position
 | 
			
		||||
          j.meta do |j|
 | 
			
		||||
            j.id track.id
 | 
			
		||||
            j.title track.title
 | 
			
		||||
            j.duration track.duration
 | 
			
		||||
            j.length track.length
 | 
			
		||||
            j.artists track.artists.map(&:name)
 | 
			
		||||
            j.album album.title
 | 
			
		||||
            j.album_pic album.pic_safe
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def import
 | 
			
		||||
    return unless rovi_id?
 | 
			
		||||
 | 
			
		||||
    Artist.import(Robbie::Artist.find(rovi_id))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
@ -43,7 +61,7 @@ class Artist < ActiveRecord::Base
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def import(rovi_artist)
 | 
			
		||||
      data = BeatParser::Aggregator.new.combine(rovi_artist.id)
 | 
			
		||||
      data = BeatParser::Aggregator.new.artist(rovi_artist.id)
 | 
			
		||||
      artist = Artist.find_or_create_by_rovi_id(data[:id])
 | 
			
		||||
      artist.update_attributes(
 | 
			
		||||
        name: data[:name],
 | 
			
		||||
@ -83,6 +101,7 @@ class Artist < ActiveRecord::Base
 | 
			
		||||
        )
 | 
			
		||||
        ArtistGenre.find_or_create_by_artist_id_and_genre_id(artist.id, genre.id)
 | 
			
		||||
      end
 | 
			
		||||
      artist
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,25 @@
 | 
			
		||||
    <div class="navbar-inner">
 | 
			
		||||
      <div class="container">
 | 
			
		||||
        <a class="brand" href="">BeatHaven</a>
 | 
			
		||||
        <form class="navbar-search pull-left">
 | 
			
		||||
          <input type="search" placeholder="Search" class="search-query">
 | 
			
		||||
        </form>
 | 
			
		||||
        <div class="player pull-right">
 | 
			
		||||
          <div class="controls">
 | 
			
		||||
            <a href="" class="btn btn-bh-dark prev"><i class="icon-backward icon-white"></i></a>
 | 
			
		||||
            <a href="" class="btn btn-bh-dark pause"><i class="icon-pause icon-white"></i></a>
 | 
			
		||||
            <a href="" class="btn btn-bh-dark next"><i class="icon-forward icon-white"></i></a>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="progress-bar">
 | 
			
		||||
            <div class="bar" style="width: 42%"></div>
 | 
			
		||||
            <div class="title">Foo Fighters — Alone + Easy Target</div>
 | 
			
		||||
            <div class="move-it"></div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="container" id="main">Loading...</div>
 | 
			
		||||
  <div class="container" id="main">You can't steal what's free</div>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
@ -3,5 +3,7 @@ lastfm:
 | 
			
		||||
  api_secret: secret
 | 
			
		||||
  client_name: BeatHaven
 | 
			
		||||
rovi:
 | 
			
		||||
  api_key: secret
 | 
			
		||||
  api_secret: secret
 | 
			
		||||
  meta_api_key: secret
 | 
			
		||||
  meta_api_secret: secret
 | 
			
		||||
  autocomplete_api_key: secret
 | 
			
		||||
  autocomplete_api_secret: secret
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,6 @@ File.open("#{Rails.root}/config/api_keys.yml") do |file|
 | 
			
		||||
  LastFM.secret = config["lastfm"]["api_secret"]
 | 
			
		||||
  LastFM.client_name = config["lastfm"]["client_name"]
 | 
			
		||||
 | 
			
		||||
  Robbie.setup(
 | 
			
		||||
    api_key: config["rovi"]["api_key"],
 | 
			
		||||
    api_secret: config["rovi"]["api_secret"]
 | 
			
		||||
  )
 | 
			
		||||
  Robbie.setup(config["rovi"].symbolize_keys)
 | 
			
		||||
  Robbie.enable_cache
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,14 @@
 | 
			
		||||
BeatHaven::Application.routes.draw do
 | 
			
		||||
  namespace :api do
 | 
			
		||||
    resources :artists, only: [:show], constraints: { id: /.+/ }
 | 
			
		||||
    resources :albums, only: [:picture] do
 | 
			
		||||
    resources :albums, only: [:show, :picture] do
 | 
			
		||||
      member { get :picture }
 | 
			
		||||
    end
 | 
			
		||||
    resources :search, only: [] do
 | 
			
		||||
      collection { get :complete; get :wtfis }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  match "/:path" => "application#main", constraints: { path: /.*/ }
 | 
			
		||||
  root to: "application#main"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
class AddIsHiddenFieldToAlbums < ActiveRecord::Migration
 | 
			
		||||
  def change
 | 
			
		||||
    add_column :albums, :is_hidden, :boolean, default: false
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -11,7 +11,7 @@
 | 
			
		||||
#
 | 
			
		||||
# It's strongly recommended to check this file into your version control system.
 | 
			
		||||
 | 
			
		||||
ActiveRecord::Schema.define(:version => 20120826172120) do
 | 
			
		||||
ActiveRecord::Schema.define(:version => 20120901111329) do
 | 
			
		||||
 | 
			
		||||
  create_table "albums", :force => true do |t|
 | 
			
		||||
    t.integer  "artist_id"
 | 
			
		||||
@ -19,8 +19,9 @@ ActiveRecord::Schema.define(:version => 20120826172120) do
 | 
			
		||||
    t.string   "title"
 | 
			
		||||
    t.integer  "year"
 | 
			
		||||
    t.string   "pic"
 | 
			
		||||
    t.datetime "created_at", :null => false
 | 
			
		||||
    t.datetime "updated_at", :null => false
 | 
			
		||||
    t.datetime "created_at",                    :null => false
 | 
			
		||||
    t.datetime "updated_at",                    :null => false
 | 
			
		||||
    t.boolean  "is_hidden",  :default => false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  create_table "artist_genres", :force => true do |t|
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										433
									
								
								vendor/assets/javascripts/jquery.autocomplete.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										433
									
								
								vendor/assets/javascripts/jquery.autocomplete.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,433 @@
 | 
			
		||||
/**
 | 
			
		||||
*  Ajax Autocomplete for jQuery, version 1.1.5
 | 
			
		||||
*  (c) 2010 Tomas Kirda, Vytautas Pranskunas
 | 
			
		||||
*
 | 
			
		||||
*  Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
 | 
			
		||||
*  For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/
 | 
			
		||||
*
 | 
			
		||||
*  Last Review: 07/24/2012
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*jslint onevar: true, evil: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */
 | 
			
		||||
/*global window: true, document: true, clearInterval: true, setInterval: true, jQuery: true */
 | 
			
		||||
 | 
			
		||||
(function ($) {
 | 
			
		||||
 | 
			
		||||
	var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g');
 | 
			
		||||
 | 
			
		||||
	function fnFormatResult(value, data, currentValue) {
 | 
			
		||||
		var pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')';
 | 
			
		||||
		return value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function Autocomplete(el, options) {
 | 
			
		||||
		this.el = $(el);
 | 
			
		||||
		this.el.attr('autocomplete', 'off');
 | 
			
		||||
		this.suggestions = [];
 | 
			
		||||
		this.data = [];
 | 
			
		||||
		this.badQueries = [];
 | 
			
		||||
		this.selectedIndex = -1;
 | 
			
		||||
		this.currentValue = this.el.val();
 | 
			
		||||
		this.intervalId = 0;
 | 
			
		||||
		this.cachedResponse = [];
 | 
			
		||||
		this.onChangeInterval = null;
 | 
			
		||||
		this.onChange = null;
 | 
			
		||||
		this.ignoreValueChange = false;
 | 
			
		||||
		this.serviceUrl = options.serviceUrl;
 | 
			
		||||
		this.isLocal = false;
 | 
			
		||||
		this.options = {
 | 
			
		||||
			autoSubmit: false,
 | 
			
		||||
			minChars: 1,
 | 
			
		||||
			maxHeight: 300,
 | 
			
		||||
			deferRequestBy: 0,
 | 
			
		||||
			width: 0,
 | 
			
		||||
			highlight: true,
 | 
			
		||||
			params: {},
 | 
			
		||||
			fnFormatResult: fnFormatResult,
 | 
			
		||||
			delimiter: null,
 | 
			
		||||
			zIndex: 9999
 | 
			
		||||
		};
 | 
			
		||||
		this.initialize();
 | 
			
		||||
		this.setOptions(options);
 | 
			
		||||
		this.el.data('autocomplete', this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$.fn.autocomplete = function (options, optionName) {
 | 
			
		||||
 | 
			
		||||
		var autocompleteControl;
 | 
			
		||||
 | 
			
		||||
		if (typeof options == 'string') {
 | 
			
		||||
			autocompleteControl = this.data('autocomplete');
 | 
			
		||||
			if (typeof autocompleteControl[options] == 'function') {
 | 
			
		||||
				autocompleteControl[options](optionName);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			autocompleteControl = new Autocomplete(this.get(0) || $('<input />'), options);
 | 
			
		||||
		}
 | 
			
		||||
		return autocompleteControl;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	Autocomplete.prototype = {
 | 
			
		||||
 | 
			
		||||
		killerFn: null,
 | 
			
		||||
 | 
			
		||||
		initialize: function () {
 | 
			
		||||
 | 
			
		||||
			var me, uid, autocompleteElId;
 | 
			
		||||
			me = this;
 | 
			
		||||
			uid = Math.floor(Math.random() * 0x100000).toString(16);
 | 
			
		||||
			autocompleteElId = 'Autocomplete_' + uid;
 | 
			
		||||
 | 
			
		||||
			this.killerFn = function (e) {
 | 
			
		||||
				if ($(e.target).parents('.autocomplete').size() === 0) {
 | 
			
		||||
					me.killSuggestions();
 | 
			
		||||
					me.disableKillerFn();
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			if (!this.options.width) { this.options.width = this.el.width(); }
 | 
			
		||||
			this.mainContainerId = 'AutocompleteContainter_' + uid;
 | 
			
		||||
 | 
			
		||||
			$('<div id="' + this.mainContainerId + '" style="position:absolute;z-index:9999;"><div class="autocomplete-w1"><div class="autocomplete" id="' + autocompleteElId + '" style="display:none; width:300px;"></div></div></div>').appendTo('body');
 | 
			
		||||
 | 
			
		||||
			this.container = $('#' + autocompleteElId);
 | 
			
		||||
			this.fixPosition();
 | 
			
		||||
			if (window.opera) {
 | 
			
		||||
				this.el.keypress(function (e) { me.onKeyPress(e); });
 | 
			
		||||
			} else {
 | 
			
		||||
				this.el.keydown(function (e) { me.onKeyPress(e); });
 | 
			
		||||
			}
 | 
			
		||||
			this.el.keyup(function (e) { me.onKeyUp(e); });
 | 
			
		||||
			this.el.blur(function () { me.enableKillerFn(); });
 | 
			
		||||
			this.el.focus(function () { me.fixPosition(); });
 | 
			
		||||
			this.el.change(function () { me.onValueChanged(); });
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		extendOptions: function (options) {
 | 
			
		||||
			$.extend(this.options, options);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		setOptions: function (options) {
 | 
			
		||||
			var o = this.options;
 | 
			
		||||
			this.extendOptions(options);
 | 
			
		||||
			if (o.lookup || o.isLocal) {
 | 
			
		||||
				this.isLocal = true;
 | 
			
		||||
				if ($.isArray(o.lookup)) { o.lookup = { suggestions: o.lookup, data: [] }; }
 | 
			
		||||
			}
 | 
			
		||||
			$('#' + this.mainContainerId).css({ zIndex: o.zIndex });
 | 
			
		||||
			this.container.css({ maxHeight: o.maxHeight + 'px', width: o.width });
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		clearCache: function () {
 | 
			
		||||
			this.cachedResponse = [];
 | 
			
		||||
			this.badQueries = [];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disable: function () {
 | 
			
		||||
			this.disabled = true;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		enable: function () {
 | 
			
		||||
			this.disabled = false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		fixPosition: function () {
 | 
			
		||||
			var offset = this.el.offset();
 | 
			
		||||
			$('#' + this.mainContainerId).css({ top: (offset.top + this.el.innerHeight()) + 'px', left: offset.left + 'px' });
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		enableKillerFn: function () {
 | 
			
		||||
			var me = this;
 | 
			
		||||
			$(document).bind('click', me.killerFn);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disableKillerFn: function () {
 | 
			
		||||
			var me = this;
 | 
			
		||||
			$(document).unbind('click', me.killerFn);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		killSuggestions: function () {
 | 
			
		||||
			var me = this;
 | 
			
		||||
			this.stopKillSuggestions();
 | 
			
		||||
			this.intervalId = window.setInterval(function () { me.hide(); me.stopKillSuggestions(); }, 300);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		stopKillSuggestions: function () {
 | 
			
		||||
			window.clearInterval(this.intervalId);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onValueChanged: function () {
 | 
			
		||||
			this.change(this.selectedIndex);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onKeyPress: function (e) {
 | 
			
		||||
			if (this.disabled || !this.enabled) { return; }
 | 
			
		||||
			// return will exit the function
 | 
			
		||||
			// and event will not be prevented
 | 
			
		||||
			switch (e.keyCode) {
 | 
			
		||||
				case 27: //KEY_ESC:
 | 
			
		||||
					this.el.val(this.currentValue);
 | 
			
		||||
					this.hide();
 | 
			
		||||
					break;
 | 
			
		||||
				case 9: //KEY_TAB:
 | 
			
		||||
				case 13: //KEY_RETURN:
 | 
			
		||||
					if (this.selectedIndex === -1) {
 | 
			
		||||
						this.hide();
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
					this.select(this.selectedIndex);
 | 
			
		||||
					if (e.keyCode === 9) { return; }
 | 
			
		||||
					break;
 | 
			
		||||
				case 38: //KEY_UP:
 | 
			
		||||
					this.moveUp();
 | 
			
		||||
					break;
 | 
			
		||||
				case 40: //KEY_DOWN:
 | 
			
		||||
					this.moveDown();
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					return;
 | 
			
		||||
			}
 | 
			
		||||
			e.stopImmediatePropagation();
 | 
			
		||||
			e.preventDefault();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onKeyUp: function (e) {
 | 
			
		||||
			if (this.disabled) { return; }
 | 
			
		||||
			switch (e.keyCode) {
 | 
			
		||||
				case 38: //KEY_UP:
 | 
			
		||||
				case 40: //KEY_DOWN:
 | 
			
		||||
					return;
 | 
			
		||||
			}
 | 
			
		||||
			clearInterval(this.onChangeInterval);
 | 
			
		||||
			if (this.currentValue !== this.el.val()) {
 | 
			
		||||
				if (this.options.deferRequestBy > 0) {
 | 
			
		||||
					// Defer lookup in case when value changes very quickly:
 | 
			
		||||
					var me = this;
 | 
			
		||||
					this.onChangeInterval = setInterval(function () { me.onValueChange(); }, this.options.deferRequestBy);
 | 
			
		||||
				} else {
 | 
			
		||||
					this.onValueChange();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onValueChange: function () {
 | 
			
		||||
			clearInterval(this.onChangeInterval);
 | 
			
		||||
			this.currentValue = this.el.val();
 | 
			
		||||
			var q = this.getQuery(this.currentValue);
 | 
			
		||||
			this.selectedIndex = -1;
 | 
			
		||||
			if (this.ignoreValueChange) {
 | 
			
		||||
				this.ignoreValueChange = false;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			if (q === '' || q.length < this.options.minChars) {
 | 
			
		||||
				this.hide();
 | 
			
		||||
			} else {
 | 
			
		||||
				this.getSuggestions(q);
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		getQuery: function (val) {
 | 
			
		||||
			var d, arr;
 | 
			
		||||
			d = this.options.delimiter;
 | 
			
		||||
			if (!d) { return $.trim(val); }
 | 
			
		||||
			arr = val.split(d);
 | 
			
		||||
			return $.trim(arr[arr.length - 1]);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		getSuggestionsLocal: function (q) {
 | 
			
		||||
			var ret, arr, len, val, i;
 | 
			
		||||
			arr = this.options.lookup;
 | 
			
		||||
			len = arr.suggestions.length;
 | 
			
		||||
			ret = { suggestions: [], data: [] };
 | 
			
		||||
			q = q.toLowerCase();
 | 
			
		||||
			for (i = 0; i < len; i++) {
 | 
			
		||||
				val = arr.suggestions[i];
 | 
			
		||||
				if (val.toLowerCase().indexOf(q) === 0) {
 | 
			
		||||
					ret.suggestions.push(val);
 | 
			
		||||
					ret.data.push(arr.data[i]);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return ret;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		getSuggestions: function (q) {
 | 
			
		||||
 | 
			
		||||
			var cr, me;
 | 
			
		||||
			cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q]; //dadeta this.options.isLocal ||
 | 
			
		||||
			if (cr && $.isArray(cr.suggestions)) {
 | 
			
		||||
				this.suggestions = cr.suggestions;
 | 
			
		||||
				this.data = cr.data;
 | 
			
		||||
				this.suggest();
 | 
			
		||||
			} else if (!this.isBadQuery(q)) {
 | 
			
		||||
				me = this;
 | 
			
		||||
				me.options.params.query = q;
 | 
			
		||||
				$.get(this.serviceUrl, me.options.params, function (txt) { me.processResponse(txt); }, 'text');
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		isBadQuery: function (q) {
 | 
			
		||||
			var i = this.badQueries.length;
 | 
			
		||||
			while (i--) {
 | 
			
		||||
				if (q.indexOf(this.badQueries[i]) === 0) { return true; }
 | 
			
		||||
			}
 | 
			
		||||
			return false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		hide: function () {
 | 
			
		||||
			this.enabled = false;
 | 
			
		||||
			this.selectedIndex = -1;
 | 
			
		||||
			this.container.hide();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		suggest: function () {
 | 
			
		||||
 | 
			
		||||
			if (this.suggestions.length === 0) {
 | 
			
		||||
				this.hide();
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var me, len, div, f, v, i, s, mOver, mClick;
 | 
			
		||||
			me = this;
 | 
			
		||||
			len = this.suggestions.length;
 | 
			
		||||
			f = this.options.fnFormatResult;
 | 
			
		||||
			v = this.getQuery(this.currentValue);
 | 
			
		||||
			mOver = function (xi) { return function () { me.activate(xi); }; };
 | 
			
		||||
			mClick = function (xi) { return function () { me.select(xi); }; };
 | 
			
		||||
			this.container.hide().empty();
 | 
			
		||||
			for (i = 0; i < len; i++) {
 | 
			
		||||
				s = this.suggestions[i];
 | 
			
		||||
				div = $((me.selectedIndex === i ? '<div class="selected"' : '<div') + ' title="' + s + '">' + f(s, this.data[i], v) + '</div>');
 | 
			
		||||
				div.mouseover(mOver(i));
 | 
			
		||||
				div.click(mClick(i));
 | 
			
		||||
				this.container.append(div);
 | 
			
		||||
			}
 | 
			
		||||
			this.enabled = true;
 | 
			
		||||
			this.container.show();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		processResponse: function (text) {
 | 
			
		||||
			var response;
 | 
			
		||||
			try {
 | 
			
		||||
				response = eval('(' + text + ')');
 | 
			
		||||
			} catch (err) { return; }
 | 
			
		||||
			if (!$.isArray(response.data)) { response.data = []; }
 | 
			
		||||
			if (!this.options.noCache) {
 | 
			
		||||
				this.cachedResponse[response.query] = response;
 | 
			
		||||
				if (response.suggestions.length === 0) { this.badQueries.push(response.query); }
 | 
			
		||||
			}
 | 
			
		||||
			if (response.query === this.getQuery(this.currentValue)) {
 | 
			
		||||
				this.suggestions = response.suggestions;
 | 
			
		||||
				this.data = response.data;
 | 
			
		||||
				this.suggest();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		activate: function (index) {
 | 
			
		||||
			var divs, activeItem;
 | 
			
		||||
			divs = this.container.children();
 | 
			
		||||
			// Clear previous selection:
 | 
			
		||||
			if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {
 | 
			
		||||
				$(divs.get(this.selectedIndex)).removeClass();
 | 
			
		||||
			}
 | 
			
		||||
			this.selectedIndex = index;
 | 
			
		||||
			if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {
 | 
			
		||||
				activeItem = divs.get(this.selectedIndex);
 | 
			
		||||
				$(activeItem).addClass('selected');
 | 
			
		||||
			}
 | 
			
		||||
			return activeItem;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		deactivate: function (div, index) {
 | 
			
		||||
			div.className = '';
 | 
			
		||||
			if (this.selectedIndex === index) { this.selectedIndex = -1; }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		select: function (i) {
 | 
			
		||||
			var selectedValue, f;
 | 
			
		||||
			selectedValue = this.suggestions[i];
 | 
			
		||||
			if (selectedValue) {
 | 
			
		||||
				this.el.val(selectedValue);
 | 
			
		||||
				if (this.options.autoSubmit) {
 | 
			
		||||
					f = this.el.parents('form');
 | 
			
		||||
					if (f.length > 0) { f.get(0).submit(); }
 | 
			
		||||
				}
 | 
			
		||||
				this.ignoreValueChange = true;
 | 
			
		||||
				this.hide();
 | 
			
		||||
				this.onSelect(i);
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		change: function (i) {
 | 
			
		||||
			var selectedValue, fn, me;
 | 
			
		||||
			me = this;
 | 
			
		||||
			selectedValue = this.suggestions[i];
 | 
			
		||||
			if (selectedValue) {
 | 
			
		||||
				var s, d;
 | 
			
		||||
				s = me.suggestions[i];
 | 
			
		||||
				d = me.data[i];
 | 
			
		||||
				me.el.val(me.getValue(s));
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				s = '';
 | 
			
		||||
				d = -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fn = me.options.onChange;
 | 
			
		||||
			if ($.isFunction(fn)) { fn(s, d, me.el); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		moveUp: function () {
 | 
			
		||||
			if (this.selectedIndex === -1) { return; }
 | 
			
		||||
			if (this.selectedIndex === 0) {
 | 
			
		||||
				this.container.children().get(0).className = '';
 | 
			
		||||
				this.selectedIndex = -1;
 | 
			
		||||
				this.el.val(this.currentValue);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			this.adjustScroll(this.selectedIndex - 1);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		moveDown: function () {
 | 
			
		||||
			if (this.selectedIndex === (this.suggestions.length - 1)) { return; }
 | 
			
		||||
			this.adjustScroll(this.selectedIndex + 1);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		adjustScroll: function (i) {
 | 
			
		||||
			var activeItem, offsetTop, upperBound, lowerBound;
 | 
			
		||||
			activeItem = this.activate(i);
 | 
			
		||||
			offsetTop = activeItem.offsetTop;
 | 
			
		||||
			upperBound = this.container.scrollTop();
 | 
			
		||||
			lowerBound = upperBound + this.options.maxHeight - 25;
 | 
			
		||||
			if (offsetTop < upperBound) {
 | 
			
		||||
				this.container.scrollTop(offsetTop);
 | 
			
		||||
			} else if (offsetTop > lowerBound) {
 | 
			
		||||
				this.container.scrollTop(offsetTop - this.options.maxHeight + 25);
 | 
			
		||||
			}
 | 
			
		||||
			this.el.val(this.getValue(this.suggestions[i]));
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onSelect: function (i) {
 | 
			
		||||
			var me, fn, s, d;
 | 
			
		||||
			me = this;
 | 
			
		||||
			fn = me.options.onSelect;
 | 
			
		||||
			s = me.suggestions[i];
 | 
			
		||||
			d = me.data[i];
 | 
			
		||||
			me.el.val(me.getValue(s));
 | 
			
		||||
			if ($.isFunction(fn)) { fn(s, d, me.el); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		getValue: function (value) {
 | 
			
		||||
			var del, currVal, arr, me;
 | 
			
		||||
			me = this;
 | 
			
		||||
			del = me.options.delimiter;
 | 
			
		||||
			if (!del) { return value; }
 | 
			
		||||
			currVal = me.currentValue;
 | 
			
		||||
			arr = currVal.split(del);
 | 
			
		||||
			if (arr.length === 1) { return value; }
 | 
			
		||||
			return currVal.substr(0, currVal.length - arr[arr.length - 1].length) + value;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
} (jQuery));
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user