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 .
|
||||
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
class BeatHaven.Models.Album extends Backbone.Model
|
||||
urlRoot: "/api/albums"
|
|
@ -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}")
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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…
Reference in New Issue