diff --git a/index.html b/index.html index cd85f0c..c4e3926 100644 --- a/index.html +++ b/index.html @@ -6,8 +6,8 @@ com.podnoms.settings = { REALTIME_HOST: 'www.deepsouthsounds.com:8081', REALTIME_PORT: 'www.deepsouthsounds.com:8081', urlRoot: '/api/v1/', - liveStreamRoot: 'http://radio.deepsouthsounds.com:8000/mp3', - streamInfoUrl: 'http://radio.deepsouthsounds.com:8000/mp3', + liveStreamRoot: 'http://rad.deepsouthsounds.com:8000/dss_320_mp3', + streamInfoUrl: 'http://rad.deepsouthsounds.com:8000/dss_320_mp3', volume: '50', smDebugMode: false, isDebug: false, diff --git a/manage.py b/manage.py old mode 100755 new mode 100644 diff --git a/static/css/bootstrap-player.css b/static/css/bootstrap-player.css new file mode 100644 index 0000000..a3e4c48 --- /dev/null +++ b/static/css/bootstrap-player.css @@ -0,0 +1,69 @@ +.playa input[type=range] { + -webkit-appearance:none; + -moz-apperance:none; + background-color:#ddd; + border: 1px solid #bbb; + height:.5em; + border-radius:.5em; + width:100%; + margin:-2px; + padding:0 !important; + line-height:0; +} +.playa input[type=range]::-webkit-slider-thumb { + -webkit-appearance:none; + -moz-apperance:none; + z-index:3; + height:1em; + width:1em; + border-radius:1em; + border:1px solid #aaa; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fefefe), color-stop(0.49, #dddddd), color-stop(0.51, #d1d1d1), color-stop(1, #a1a1a1)); +} + +.playa { + /*margin:0;*/ +} + +.playa .thumbnail { + width:96%; + padding:1.5%; +} + +.playa .btn-group { + text-align:center; +} + +@media (max-width: 979px) and (min-width: 768px){ + .span4 .playa, .playa.span4{ + padding:0; + } +} +@media (min-width: 768px){ + .playa .btn-group .btn { + padding:4px 3px; + margin:auto; + display:inline-block; + float:none; + } + .playa .btn-group .span1 { + width:8%; + } + .playa .btn-group .span2 { + width:16%; + } + .playa .btn-group .span3 { + width:25%; + } + .playa .btn-group .span4{ + width:33%; + } + .playa.span2 .btn-group .btn, .playa.span1 .btn-group .btn, .span2 .playa .btn-group .btn, .span1 .playa .btn-group .btn{ + width:100%; + display:block; + } + .span4.playa, .span3 .playa, .span2 .playa, .span1 .playa, .playa.span4, .playa.span3, .playa.span2, .playa.span1{ + padding:0; + overflow:hidden; + } +} diff --git a/static/img/radio.jpg b/static/img/radio.jpg new file mode 100644 index 0000000..b42a9df Binary files /dev/null and b/static/img/radio.jpg differ diff --git a/static/js/app/lib/audioController.coffee b/static/js/app/lib/audioController.coffee index 08834fd..75bfb87 100644 --- a/static/js/app/lib/audioController.coffee +++ b/static/js/app/lib/audioController.coffee @@ -7,6 +7,8 @@ define ['app', 'marionette', 'vent', 'utils'], @listenTo(vent, 'mix:init', @mixInit) @listenTo(vent, 'mix:pause', @mixPause) @listenTo(vent, 'mix:play', @mixPlay) + @listenTo(vent, 'live:play', @livePlay) + @listenTo(vent, 'live:pause', @livePause) mixInit: (model) => console.log "AudioController: mixInit" @@ -32,5 +34,13 @@ define ['app', 'marionette', 'vent', 'utils'], mixPause: (model) -> console.log("AudioController: mixPause") com.podnoms.player.pause(); + + livePlay: -> + console.log("AudioController: livePlay") + com.podnoms.player.playLive + success: -> + console.log("Live stream started") + vent.trigger('live:started') + AudioController diff --git a/static/js/app/lib/audioController.js b/static/js/app/lib/audioController.js index 60e33b9..4af7cc7 100644 --- a/static/js/app/lib/audioController.js +++ b/static/js/app/lib/audioController.js @@ -1,30 +1,33 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['app', 'marionette', 'vent', 'utils'], function(App, Marionette, vent, utils) { - var AudioController; - AudioController = (function(_super) { + var AudioController, _ref; + AudioController = (function(_super) { __extends(AudioController, _super); function AudioController() { - this.mixInit = __bind(this.mixInit, this); - return AudioController.__super__.constructor.apply(this, arguments); + this.mixInit = __bind(this.mixInit, this); _ref = AudioController.__super__.constructor.apply(this, arguments); + return _ref; } AudioController.prototype.initialize = function(options) { console.log("AudioController: initialize"); this.listenTo(vent, 'mix:init', this.mixInit); this.listenTo(vent, 'mix:pause', this.mixPause); - return this.listenTo(vent, 'mix:play', this.mixPlay); + this.listenTo(vent, 'mix:play', this.mixPlay); + this.listenTo(vent, 'live:play', this.livePlay); + return this.listenTo(vent, 'live:pause', this.livePause); }; AudioController.prototype.mixInit = function(model) { var id, _this = this; + console.log("AudioController: mixInit"); id = model.get('id'); com.podnoms.player.stopPlaying(); @@ -53,6 +56,16 @@ return com.podnoms.player.pause(); }; + AudioController.prototype.livePlay = function() { + console.log("AudioController: livePlay"); + return com.podnoms.player.playLive({ + success: function() { + console.log("Live stream started"); + return vent.trigger('live:started'); + } + }); + }; + return AudioController; })(Marionette.Controller); diff --git a/static/js/app/lib/panningRegion.js b/static/js/app/lib/panningRegion.js index 5638e1d..febb8de 100644 --- a/static/js/app/lib/panningRegion.js +++ b/static/js/app/lib/panningRegion.js @@ -1,12 +1,14 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['marionette'], function(Marionette) { - var PanningRegion, getPrefixedCssProp; + var PanningRegion, getPrefixedCssProp, _ref; + getPrefixedCssProp = function(baseProp) { var str; + str = Modernizr.prefixed(baseProp); str = str.replace(/([A-Z])/g, function(str, m1) { return "-" + m1.toLowerCase(); @@ -14,17 +16,18 @@ return str; }; PanningRegion = (function(_super) { - __extends(PanningRegion, _super); function PanningRegion() { - return PanningRegion.__super__.constructor.apply(this, arguments); + _ref = PanningRegion.__super__.constructor.apply(this, arguments); + return _ref; } PanningRegion.prototype.el = "#content"; PanningRegion.prototype.initialize = function() { var transEndEventNames; + transEndEventNames = { WebkitTransition: "webkitTransitionEnd", MozTransition: "transitionend", @@ -40,6 +43,7 @@ PanningRegion.prototype.transitionToView = function(newView, type) { var self, view; + self = this; view = this.currentView; if (!view || view.isClosed) { @@ -49,6 +53,7 @@ Marionette.triggerMethod.call(this, "willTransition", view); newView.on("render", function() { var $background, newViewMatrix, translation, worldBgMatrix, worldContentMatrix; + self.$el.off(self.transEndEventName); translation = void 0; if (type === "slide") { diff --git a/static/js/app/lib/utils.js b/static/js/app/lib/utils.js index 4933f18..8e11d57 100644 --- a/static/js/app/lib/utils.js +++ b/static/js/app/lib/utils.js @@ -1,8 +1,8 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { - define(['jquery', 'bootstrap', 'toastr'], function($, bootstrap, toastr) { var _this = this; + return { modal: function(url) { if (url) { @@ -24,6 +24,7 @@ }, checkPlayCount: function() { var _this = this; + if (document.cookie.indexOf("sessionId")) { $.getJSON("/ajax/session_play_count", function(data) { console.log("utils: got playcount"); diff --git a/static/js/app/views/activity/activityItemView.js b/static/js/app/views/activity/activityItemView.js index 2ac89e6..ad3f7ce 100644 --- a/static/js/app/views/activity/activityItemView.js +++ b/static/js/app/views/activity/activityItemView.js @@ -1,16 +1,17 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['marionette', 'text!/tpl/ActivityListItemView'], function(Marionette, Template) { - var ActivityItemView; - return ActivityItemView = (function(_super) { + var ActivityItemView, _ref; + return ActivityItemView = (function(_super) { __extends(ActivityItemView, _super); function ActivityItemView() { - return ActivityItemView.__super__.constructor.apply(this, arguments); + _ref = ActivityItemView.__super__.constructor.apply(this, arguments); + return _ref; } ActivityItemView.prototype.template = _.template(Template); diff --git a/static/js/app/views/activity/activityListView.js b/static/js/app/views/activity/activityListView.js index 8ac3c67..cfdeef6 100644 --- a/static/js/app/views/activity/activityListView.js +++ b/static/js/app/views/activity/activityListView.js @@ -1,16 +1,17 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['marionette', 'models/activity/activityCollection', 'views/activity/activityItemView', 'text!/tpl/ActivityListView'], function(Marionette, ActivityCollection, ActivityItemView, Template) { - var ActivityListView; - ActivityListView = (function(_super) { + var ActivityListView, _ref; + ActivityListView = (function(_super) { __extends(ActivityListView, _super); function ActivityListView() { - return ActivityListView.__super__.constructor.apply(this, arguments); + _ref = ActivityListView.__super__.constructor.apply(this, arguments); + return _ref; } ActivityListView.prototype.template = _.template(Template); @@ -25,6 +26,7 @@ ActivityListView.prototype.initialize = function() { var _this = this; + console.log("ActivityListView: initialize"); this.collection = new ActivityCollection; this.collection.fetch({ diff --git a/static/js/app/views/comment/commentItemView.js b/static/js/app/views/comment/commentItemView.js index c61b6d9..8b47684 100644 --- a/static/js/app/views/comment/commentItemView.js +++ b/static/js/app/views/comment/commentItemView.js @@ -1,16 +1,17 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['marionette', 'text!/tpl/CommentItemView'], function(Marionette, Template) { - var CommentItemView; - CommentItemView = (function(_super) { + var CommentItemView, _ref; + CommentItemView = (function(_super) { __extends(CommentItemView, _super); function CommentItemView() { - return CommentItemView.__super__.constructor.apply(this, arguments); + _ref = CommentItemView.__super__.constructor.apply(this, arguments); + return _ref; } CommentItemView.prototype.template = _.template(Template); diff --git a/static/js/app/views/header.coffee b/static/js/app/views/header.coffee index 00e3cf5..aad60a7 100644 --- a/static/js/app/views/header.coffee +++ b/static/js/app/views/header.coffee @@ -6,7 +6,8 @@ Copyright (c) 2012, Fergal Moran. All rights reserved. Code provided under the BSD License: ### -define ["underscore", "backbone", "vent", "utils", "text!/tpl/HeaderView"], (_, Backbone, vent, utils, Template) -> +define ["underscore", "backbone", "vent", "utils", "text!/tpl/HeaderView"], +(_, Backbone, vent, utils, Template) -> class HeaderView extends Backbone.View template: _.template(Template) events: @@ -42,15 +43,7 @@ define ["underscore", "backbone", "vent", "utils", "text!/tpl/HeaderView"], (_, this playLive: -> - ref = this - dssSoundHandler.playLive() - _eventAggregator.trigger "track_playing" - button = $(@el).find("#header-play-pause-button") - button.data "mode", "pause" - $.getJSON "ajax/live_now_playing/", (data) -> - alert data.title - $(ref.el).find("#live-now-playing").text data.title - + vent.trigger('live:play') togglePlayState: -> button = $(@el).find("#header-play-pause-button") diff --git a/static/js/app/views/header.js b/static/js/app/views/header.js index 08314cb..d694ae4 100644 --- a/static/js/app/views/header.js +++ b/static/js/app/views/header.js @@ -1,5 +1,4 @@ -// Generated by CoffeeScript 1.3.3 - +// Generated by CoffeeScript 1.6.2 /* @license @@ -15,13 +14,14 @@ Code provided under the BSD License: __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(["underscore", "backbone", "vent", "utils", "text!/tpl/HeaderView"], function(_, Backbone, vent, utils, Template) { - var HeaderView; - HeaderView = (function(_super) { + var HeaderView, _ref; + HeaderView = (function(_super) { __extends(HeaderView, _super); function HeaderView() { - return HeaderView.__super__.constructor.apply(this, arguments); + _ref = HeaderView.__super__.constructor.apply(this, arguments); + return _ref; } HeaderView.prototype.template = _.template(Template); @@ -67,20 +67,12 @@ Code provided under the BSD License: }; HeaderView.prototype.playLive = function() { - var button, ref; - ref = this; - dssSoundHandler.playLive(); - _eventAggregator.trigger("track_playing"); - button = $(this.el).find("#header-play-pause-button"); - button.data("mode", "pause"); - return $.getJSON("ajax/live_now_playing/", function(data) { - alert(data.title); - return $(ref.el).find("#live-now-playing").text(data.title); - }); + return vent.trigger('live:play'); }; HeaderView.prototype.togglePlayState = function() { var button, mode; + button = $(this.el).find("#header-play-pause-button"); mode = button.data("mode"); if (mode === "play") { diff --git a/static/js/app/views/mix/mixItemView.js b/static/js/app/views/mix/mixItemView.js index 2f504e7..31a85a1 100644 --- a/static/js/app/views/mix/mixItemView.js +++ b/static/js/app/views/mix/mixItemView.js @@ -1,26 +1,22 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['moment', 'app', 'vent', 'marionette', 'models/comment/commentCollection', 'views/comment/commentListView', 'text!/tpl/MixListItemView'], function(moment, App, vent, Marionette, CommentsCollection, CommentsListView, Template) { - var MixItemView; - MixItemView = (function(_super) { + var MixItemView, _ref; + MixItemView = (function(_super) { __extends(MixItemView, _super); function MixItemView() { this.doStart = __bind(this.doStart, this); - this.renderComments = __bind(this.renderComments, this); - this.renderGenres = __bind(this.renderGenres, this); - this.onRender = __bind(this.onRender, this); - - this.initialize = __bind(this.initialize, this); - return MixItemView.__super__.constructor.apply(this, arguments); + this.initialize = __bind(this.initialize, this); _ref = MixItemView.__super__.constructor.apply(this, arguments); + return _ref; } MixItemView.prototype.template = _.template(Template); @@ -54,6 +50,7 @@ MixItemView.prototype.onRender = function() { var id, totalDuration, totalDurationText; + id = this.model.get('id'); if (this.model.get('duration')) { totalDuration = moment.duration(this.model.get('duration'), "seconds"); @@ -73,6 +70,7 @@ MixItemView.prototype.renderGenres = function() { var el; + el = this.el; $.each(this.model.get("genre-list"), function(data) { $("#genre-list", el).append('' + this.text + ''); @@ -83,6 +81,7 @@ MixItemView.prototype.renderComments = function() { var comments; + comments = new CommentsCollection(); comments.url = this.model.get("resource_uri") + "comments/"; comments.mix_id = this.model.id; @@ -90,6 +89,7 @@ comments.fetch({ success: function(data) { var content; + console.log(data); content = new CommentsListView({ collection: comments @@ -133,6 +133,7 @@ MixItemView.prototype.mixFavourite = function() { var app; + console.log("MixItemView: favouriteMix"); app = require('app'); app.vent.trigger("mix:favourite", this.model); @@ -141,6 +142,7 @@ MixItemView.prototype.mixLike = function() { var app; + console.log("MixItemView: likeMix"); app = require('app'); app.vent.trigger("mix:like", this.model); @@ -149,6 +151,7 @@ MixItemView.prototype.mixShare = function(e) { var app, mode; + console.log("MixItemView: shareMix"); mode = $(e.currentTarget).data("mode"); console.log("MixItemView: " + mode); diff --git a/static/js/app/views/mix/mixListView.js b/static/js/app/views/mix/mixListView.js index 5df61f7..448c7c2 100644 --- a/static/js/app/views/mix/mixListView.js +++ b/static/js/app/views/mix/mixListView.js @@ -1,19 +1,20 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['marionette', 'vent', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text!/tpl/MixListView'], function(Marionette, vent, MixCollection, MixItemView, Template) { - var MixListView; + var MixListView, _ref; + MixListView = (function(_super) { var currentMix; __extends(MixListView, _super); function MixListView() { - this.initialize = __bind(this.initialize, this); - return MixListView.__super__.constructor.apply(this, arguments); + this.initialize = __bind(this.initialize, this); _ref = MixListView.__super__.constructor.apply(this, arguments); + return _ref; } MixListView.prototype.template = _.template(Template); @@ -28,6 +29,7 @@ MixListView.prototype.initialize = function() { var _this = this; + console.log("MixListView: initialize"); this.collection = new MixCollection(); this.collection.fetch({ @@ -43,6 +45,7 @@ MixListView.prototype.mixPlay = function(model) { var v; + console.log("MixListView: mixPlay"); if (currentMix !== -1) { v = this.children.findByModelCid(currentMix); diff --git a/static/js/app/views/sidebar/sidebarView.coffee b/static/js/app/views/sidebar/sidebarView.coffee index ef09405..908cfe4 100644 --- a/static/js/app/views/sidebar/sidebarView.coffee +++ b/static/js/app/views/sidebar/sidebarView.coffee @@ -13,6 +13,7 @@ define ['underscore', 'backbone', 'marionette', 'vent', 'views/activity/activity this.listenTo(vent, 'mix:init', @mixInit) this.listenTo(vent, 'mix:play', @mixPlay) this.listenTo(vent, 'mix:pause', @mixPause) + this.listenTo(vent, 'live:started', @liveStarted) return onRender: -> @@ -38,5 +39,20 @@ define ['underscore', 'backbone', 'marionette', 'vent', 'views/activity/activity $(@topRegion.el).show() @topRegion.show(new NowPlayingView({model: model})) + liveStarted: -> + console.log "SidebarView: livePlay" + $.getJSON "ajax/live_now_playing/", (data) => + $(@topRegion.el).show() + @topRegion.show(new NowPlayingView({ + model: new Backbone.Model({ + mix_image: "/static/img/radio.jpg", + item_url: "", + title: data.Description, + user_profile_url: "", + user_name: "Deep South Sounds Radio" + }) + })) + true + SidebarView diff --git a/static/js/app/views/sidebar/sidebarView.js b/static/js/app/views/sidebar/sidebarView.js index 1c76f73..b03d197 100644 --- a/static/js/app/views/sidebar/sidebarView.js +++ b/static/js/app/views/sidebar/sidebarView.js @@ -1,16 +1,17 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['underscore', 'backbone', 'marionette', 'vent', 'views/activity/activityListView', 'views/widgets/nowPlayingView', 'text!/tpl/SidebarView'], function(_, Backbone, Marionette, vent, ActivityListView, NowPlayingView, Template) { - var SidebarView; - SidebarView = (function(_super) { + var SidebarView, _ref; + SidebarView = (function(_super) { __extends(SidebarView, _super); function SidebarView() { - return SidebarView.__super__.constructor.apply(this, arguments); + _ref = SidebarView.__super__.constructor.apply(this, arguments); + return _ref; } SidebarView.prototype.template = _.template(Template); @@ -25,6 +26,7 @@ this.listenTo(vent, 'mix:init', this.mixInit); this.listenTo(vent, 'mix:play', this.mixPlay); this.listenTo(vent, 'mix:pause', this.mixPause); + this.listenTo(vent, 'live:started', this.liveStarted); }; SidebarView.prototype.onRender = function() { @@ -36,7 +38,6 @@ this.streamRegion.show(new ActivityListView()); $(this.topRegion.el).hide(); "@topRegion.show(\n new NowPlayingView(\n model: new Backbone.Model({\n item_url: \"fdskjfhdsk\", title: \"Argle bargle\", user_profile_url: \"/\", user_name: \"Foo Ferra\"\n })\n ))"; - }; SidebarView.prototype.mixInit = function(model) { @@ -47,6 +48,25 @@ })); }; + SidebarView.prototype.liveStarted = function() { + var _this = this; + + console.log("SidebarView: livePlay"); + $.getJSON("ajax/live_now_playing/", function(data) { + $(_this.topRegion.el).show(); + return _this.topRegion.show(new NowPlayingView({ + model: new Backbone.Model({ + mix_image: "/static/img/radio.jpg", + item_url: "", + title: data.Description, + user_profile_url: "", + user_name: "Deep South Sounds Radio" + }) + })); + }); + return true; + }; + return SidebarView; })(Marionette.Layout); diff --git a/static/js/app/views/user/userListView.js b/static/js/app/views/user/userListView.js index 83c4475..db84b7f 100644 --- a/static/js/app/views/user/userListView.js +++ b/static/js/app/views/user/userListView.js @@ -1,18 +1,18 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['jquery', 'marionette', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView', 'libs/bootstrap/bootpag'], function($, Marionette, UserCollection, UserItemView, Template) { - var UserListView; - UserListView = (function(_super) { + var UserListView, _ref; + UserListView = (function(_super) { __extends(UserListView, _super); function UserListView() { - this.initialize = __bind(this.initialize, this); - return UserListView.__super__.constructor.apply(this, arguments); + this.initialize = __bind(this.initialize, this); _ref = UserListView.__super__.constructor.apply(this, arguments); + return _ref; } UserListView.prototype.template = _.template(Template); @@ -23,6 +23,7 @@ UserListView.prototype.initialize = function() { var _this = this; + console.log("UserListView: initialize"); this.collection = new UserCollection(); this.collection.fetch({ diff --git a/static/js/app/views/widgets/nowPlayingView.js b/static/js/app/views/widgets/nowPlayingView.js index e4e8640..53c0212 100644 --- a/static/js/app/views/widgets/nowPlayingView.js +++ b/static/js/app/views/widgets/nowPlayingView.js @@ -1,16 +1,17 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define(['marionette', 'vent', 'text!/tpl/NowPlayingView'], function(Marionette, vent, Template) { - var NowPlayingView; - NowPlayingView = (function(_super) { + var NowPlayingView, _ref; + NowPlayingView = (function(_super) { __extends(NowPlayingView, _super); function NowPlayingView() { - return NowPlayingView.__super__.constructor.apply(this, arguments); + _ref = NowPlayingView.__super__.constructor.apply(this, arguments); + return _ref; } NowPlayingView.prototype.template = _.template(Template); diff --git a/static/js/libs/bootstrap/bootstrap-player.js b/static/js/libs/bootstrap/bootstrap-player.js new file mode 100644 index 0000000..b781f3b --- /dev/null +++ b/static/js/libs/bootstrap/bootstrap-player.js @@ -0,0 +1,313 @@ +(function($) { + 'use strict'; + $('audio[controls]').before(function(){ + var song = this; + song.controls=false; + var player_box = document.createElement('div'); + $(player_box).addClass($(song).attr('class') + ' well container-fluid playa'); + var data_sec = document.createElement('section'); + $(data_sec).addClass('collapse'); + var toggle_holder = document.createElement('div'); + $(toggle_holder).addClass('btn-group row-fluid'); + var data_toggle = document.createElement('a'); + $(data_toggle).html(''); + $(data_toggle).addClass('btn btn-block'); + $(data_toggle).attr('style', 'opacity:0.3'); + $(data_toggle).click(function (){$(data_sec).collapse('toggle');}); + $(data_toggle).attr('title', 'Details'); + $(data_toggle).tooltip({'container': 'body', 'placement': 'top', 'html': true}); + $(toggle_holder).append(data_toggle); + var data_table = document.createElement('table'); + $(data_table).addClass('table table-condensed'); + var player = document.createElement('section'); + $(player).addClass('btn-group row-fluid'); + var load_error = function(){ + console.log('error'); + $(player_box).find('.btn').addClass('disabled'); + $(player_box).find('input[type="range"]').hide(); + $(player_box).find('.icon-spin').text('Error'); + $(player_box).find('.icon-spin').parent().attr('title', 'There was an error loading the audio.'); + $(player_box).find('.icon-spin').parent().tooltip('fixTitle'); + $(player_box).find('.icon-spin').removeClass('icon-spinner icon-spin'); + }; + var addPlay = function() { + var play = document.createElement('button'); + $(play).addClass('btn disabled span1'); + play.setPlayState = function(toggle){ + $(play).removeClass('disabled'); + if (toggle === 'play') { + $(play).html(''); + $(play).click(function () { + song.play(); + }); + } + if (toggle === 'pause') { + $(play).html(''); + $(play).click(function () { + song.pause(); + }); + } + }; + $(song).on('play', function(){play.setPlayState('pause');}); + $(song).on('canplay', function(){play.setPlayState('play');}); + $(song).on('pause', function(){play.setPlayState('play');}); + var timeout = 0; + var loadCheck = setInterval(function() { + if(isNaN(song.duration) === false){ + play.setPlayState('play'); + clearInterval(loadCheck); + return true; + } + if(song.networkState === 3 || timeout === 75){ + load_error(); + clearInterval(loadCheck); + return false; + } + timeout++; + }, 50); + + $(player).append(play); + }; + var addSeek = function() { + var seek = document.createElement('input'); + $(seek).attr({ + 'type': 'range', + 'min': 0, + 'value': 0, + 'class': 'seek' + }); + seek.progress = function () { + var bg = 'rgba(223, 240, 216, 1) 0%'; + bg += ', rgba(223, 240, 216, 1) ' + ((song.currentTime/song.duration) * 100) + '%'; + bg += ', rgba(223, 240, 216, 0) ' + ((song.currentTime/song.duration) * 100) + '%'; + for (var i=0; i song.currentTime && isNaN(song.buffered.end(i)) === false && isNaN(song.buffered.start(i)) === false){ + var bufferedstart; + var bufferedend; + if (song.buffered.end(i) < song.duration) { + bufferedend = ((song.buffered.end(i)/song.duration) * 100); + } + else { + bufferedend = 100; + } + if (song.buffered.start(i) > song.currentTime){ + bufferedstart = ((song.buffered.start(i)/song.duration) * 100); + } + else { + bufferedstart = ((song.currentTime/song.duration) * 100); + } + bg += ', rgba(217, 237, 247, 0) ' + bufferedstart + '%'; + bg += ', rgba(217, 237, 247, 1) ' + bufferedstart + '%'; + bg += ', rgba(217, 237, 247, 1) ' + bufferedend + '%'; + bg += ', rgba(217, 237, 247, 0) ' + bufferedend + '%'; + } + } + $(seek).css('background', '-webkit-linear-gradient(left, ' + bg + ')'); + //These may be re-enabled when/if other browsers support the background like webkit + //$(seek).css('background','-o-linear-gradient(left, ' + bg + ')'); + //$(seek).css('background','-moz-linear-gradient(left, ' + bg + ')'); + //$(seek).css('background','-ms-linear-gradient(left, ' + bg + ')'); + //$(seek).css('background','linear-gradient(to right, ' + bg + ')'); + $(seek).css('background-color', '#ddd'); + }; + seek.set = function () { + $(seek).val(song.currentTime); + seek.progress(); + }; + seek.slide = function () { + song.currentTime = $(seek).val(); + seek.progress(); + }; + seek.init = function () { + $(seek).attr({ + 'max': song.duration, + 'step': song.duration / 100 + }); + seek.set(); + }; + seek.reset = function () { + $(seek).val(0); + song.currentTime = $(seek).val(); + if(!song.loop){song.pause();} + else {song.play();} + }; + var seek_wrapper = document.createElement('div'); + $(seek_wrapper).addClass('btn disabled span4'); + + $(seek_wrapper).append(seek); + $(seek).on('change', seek.slide); + $(song).on('timeupdate', seek.init); + $(song).on('loadedmetadata', seek.init); + $(song).on('loadeddata', seek.init); + $(song).on('progress', seek.init); + $(song).on('canplay', seek.init); + $(song).on('canplaythrough', seek.init); + $(song).on('ended', seek.reset); + if(song.readyState > 0){ + seek.init(); + } + $(player).append(seek_wrapper); + }; + var addTime = function() { + var time = document.createElement('a'); + $(time).addClass('btn span3'); + $(time).tooltip({'container': 'body', 'placement': 'right', 'html': true}); + time.twodigit = function (myNum) { + return ("0" + myNum).slice(-2); + }; + time.timesplit = function (a) { + if (isNaN(a)){return '';} + var hours = Math.floor(a / 3600); + var minutes = Math.floor(a / 60) - (hours * 60); + var seconds = Math.floor(a) - (hours * 3600) - (minutes * 60); + var timeStr = time.twodigit(minutes) + ':' + time.twodigit(seconds); + if (hours > 0) { + timeStr = hours + ':' + timeStr; + } + return timeStr; + }; + time.showtime = function () { + $(time).html(time.timesplit(song.duration)); + $(time).attr({'title': 'Click to Reset
Position: ' + (time.timesplit(song.currentTime))}); + if (!song.paused){ + $(time).html(time.timesplit(song.currentTime)); + $(time).attr({'title': 'Click to Reset
Length: ' + (time.timesplit(song.duration))}); + } + $(time).tooltip('fixTitle'); + }; + $(time).click(function () { + song.pause(); + song.currentTime = 0; + time.showtime(); + $(time).tooltip('fixTitle'); + $(time).tooltip('show'); + }); + $(time).tooltip('show'); + $(song).on('loadedmetadata', time.showtime); + $(song).on('loadeddata', time.showtime); + $(song).on('progress', time.showtime); + $(song).on('canplay', time.showtime); + $(song).on('canplaythrough', time.showtime); + $(song).on('timeupdate', time.showtime); + if(song.readyState > 0){ + time.showtime(); + } + else { + $(time).html(''); + } + $(player).append(time); + }; + var addMute = function() { + var mute = document.createElement('button'); + $(mute).addClass('btn span1'); + mute.checkVolume = function () { + if (song.volume > 0.5 && !song.muted) { + $(mute).html(''); + } else if (song.volume < 0.5 && song.volume > 0 && !song.muted) { + $(mute).html(''); + } else { + $(mute).html(''); + } + }; + $(mute).click(function () { + if (song.muted) { + song.muted = false; + song.volume = song.oldvolume; + } else { + song.muted = true; + song.oldvolume = song.volume; + song.volume = 0; + } + mute.checkVolume(); + }); + mute.checkVolume(); + $(song).on('volumechange', mute.checkVolume); + $(player).append(mute); + }; + var addVolume = function() { + var volume = document.createElement('input'); + $(volume).attr({ + 'type': 'range', + 'min': 0, + 'max': 1, + 'step': 1 / 100, + 'value': 1 + }); + volume.slide = function () { + song.muted = false; + song.volume = $(volume).val(); + }; + volume.set = function () { + $(volume).val(song.volume); + }; + var vol_wrapper = document.createElement('div'); + $(vol_wrapper).addClass('btn disabled span3'); + $(vol_wrapper).append(volume); + $(volume).on("change", volume.slide); + $(song).on('volumechange', volume.set); + $(player).append(vol_wrapper); + }; + var addAlbumArt = function() { + var albumArt = document.createElement('img'); + $(albumArt).addClass('thumbnail'); + $(albumArt).attr('src', $(song).data('infoAlbumArt')); + $(data_sec).append(albumArt); + }; + var addInfo = function(title, dataId) { + var row = document.createElement('tr'); + var head = document.createElement('th'); + var data = document.createElement('td'); + $(head).html(title); + $(data).html($(song).data(dataId)); + $(row).append(head); + $(row).append(data); + $(data_table).append(row); + }; + var addData = function() { + if (typeof($(song).data('infoAlbumArt')) !== 'undefined'){ addAlbumArt();} + if (typeof($(song).data('infoArtist')) !== 'undefined'){ addInfo('Artist', 'infoArtist');} + if (typeof($(song).data('infoTitle')) !== 'undefined'){ addInfo('Title', 'infoTitle');} + if (typeof($(song).data('infoAlbumTitle')) !== 'undefined'){ addInfo('Album', 'infoAlbumTitle');} + if (typeof($(song).data('infoLabel')) !== 'undefined'){ addInfo('Label', 'infoLabel');} + if (typeof($(song).data('infoYear')) !== 'undefined'){ addInfo('Year', 'infoYear');} + if ($(data_table).html() !== ""){ + $(data_sec).append(data_table); + $(player_box).append(toggle_holder); + $(player_box).append(data_sec); + } + }; + var addPlayer = function() { + if ($(song).data('play') !== 'off'){ addPlay();} + if ($(song).data('seek') !== 'off'){ addSeek();} + if ($(song).data('time') !== 'off'){ addTime();} + if ($(song).data('mute') !== 'off'){ addMute();} + if ($(song).data('volume') !== 'off'){ addVolume();} + $(player_box).append(player); + }; + var addAttribution = function() { + var attribution = document.createElement('small'); + $(attribution).addClass('pull-right muted'); + if (typeof($(song).data('infoAttLink')) !== 'undefined'){ + var attribution_link = document.createElement('a'); + $(attribution_link).addClass('muted'); + $(attribution_link).attr('href', $(song).data('infoAttLink')); + $(attribution_link).html($(song).data('infoAtt')); + $(attribution).append(attribution_link); + } + else { + $(attribution).html($(song).data('infoAtt')); + } + $(player_box).append(attribution); + }; + var fillPlayerBox = function() { + addData(); + addPlayer(); + if (typeof($(song).data('infoAtt')) !== 'undefined'){ addAttribution();} + }; + fillPlayerBox(); + $(song).on('error', function(){ + load_error(); + }); + return player_box; + }); +})(jQuery) \ No newline at end of file diff --git a/static/js/libs/bootstrap/html5slider.js b/static/js/libs/bootstrap/html5slider.js new file mode 100644 index 0000000..f9ff25a --- /dev/null +++ b/static/js/libs/bootstrap/html5slider.js @@ -0,0 +1,284 @@ +/* + html5slider - a JS implementation of for Firefox 16 and up + https://github.com/fryn/html5slider + + Copyright (c) 2010-2012 Frank Yan, + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +(function () { + +// test for native support + var test = document.createElement('input'); + try { + test.type = 'range'; + if (test.type == 'range') + return; + } catch (e) { + return; + } + +// test for required property support + test.style.background = 'linear-gradient(red, red)'; + if (!test.style.backgroundImage || !('MozAppearance' in test.style) || !document.mozSetImageElement || !this.MutationObserver) + return; + + var scale; + var isMac = navigator.platform == 'MacIntel'; + var thumb = { + radius: isMac ? 9 : 6, + width: isMac ? 22 : 12, + height: isMac ? 16 : 20 + }; + var track = 'linear-gradient(transparent ' + (isMac ? + '6px, #999 6px, #999 7px, #ccc 8px, #bbb 9px, #bbb 10px, transparent 10px' : + '9px, #999 9px, #bbb 10px, #fff 11px, transparent 11px') + + ', transparent)'; + var styles = { + 'min-width': thumb.width + 'px', + 'min-height': thumb.height + 'px', + 'max-height': thumb.height + 'px', + padding: '0 0 ' + (isMac ? '2px' : '1px'), + border: 0, + 'border-radius': 0, + cursor: 'default', + 'text-indent': '-999999px' // -moz-user-select: none; breaks mouse capture + }; + var options = { + attributes: true, + attributeFilter: ['min', 'max', 'step', 'value'] + }; + var forEach = Array.prototype.forEach; + var onChange = document.createEvent('HTMLEvents'); + onChange.initEvent('change', true, false); + + if (document.readyState == 'loading') + document.addEventListener('DOMContentLoaded', initialize, true); + else + initialize(); + + function initialize() { + // create initial sliders + forEach.call(document.querySelectorAll('input[type=range]'), transform); + // create sliders on-the-fly + new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.addedNodes) + forEach.call(mutation.addedNodes, function (node) { + check(node); + if (node.childElementCount) + forEach.call(node.querySelectorAll('input'), check); + }); + }); + }).observe(document, { childList: true, subtree: true }); + } + + function check(input) { + if (input.localName == 'input' && input.type != 'range' && + input.getAttribute('type') == 'range') + transform(input); + } + + function transform(slider) { + + var isValueSet, areAttrsSet, isChanged, isClick, prevValue, rawValue, prevX; + var min, max, step, range, value = slider.value; + + // lazily create shared slider affordance + if (!scale) { + scale = document.body.appendChild(document.createElement('hr')); + style(scale, { + '-moz-appearance': isMac ? 'scale-horizontal' : 'scalethumb-horizontal', + display: 'block', + visibility: 'visible', + opacity: 1, + position: 'fixed', + top: '-999999px' + }); + document.mozSetImageElement('__sliderthumb__', scale); + } + + // reimplement value and type properties + var getValue = function () { + return '' + value; + }; + var setValue = function setValue(val) { + value = '' + val; + isValueSet = true; + draw(); + delete slider.value; + slider.value = value; + slider.__defineGetter__('value', getValue); + slider.__defineSetter__('value', setValue); + }; + slider.__defineGetter__('value', getValue); + slider.__defineSetter__('value', setValue); + slider.__defineGetter__('type', function () { + return 'range'; + }); + + // sync properties with attributes + ['min', 'max', 'step'].forEach(function (prop) { + if (slider.hasAttribute(prop)) + areAttrsSet = true; + slider.__defineGetter__(prop, function () { + return this.hasAttribute(prop) ? this.getAttribute(prop) : ''; + }); + slider.__defineSetter__(prop, function (val) { + val === null ? this.removeAttribute(prop) : this.setAttribute(prop, val); + }); + }); + + // initialize slider + slider.readOnly = true; + style(slider, styles); + update(); + + new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.attributeName != 'value') { + update(); + areAttrsSet = true; + } + // note that value attribute only sets initial value + else if (!isValueSet) { + value = slider.getAttribute('value'); + draw(); + } + }); + }).observe(slider, options); + + slider.addEventListener('mousedown', onDragStart, true); + slider.addEventListener('keydown', onKeyDown, true); + slider.addEventListener('focus', onFocus, true); + slider.addEventListener('blur', onBlur, true); + + function onDragStart(e) { + isClick = true; + setTimeout(function () { + isClick = false; + }, 0); + if (e.button || !range) + return; + var width = parseFloat(getComputedStyle(this, 0).width); + var multiplier = (width - thumb.width) / range; + if (!multiplier) + return; + // distance between click and center of thumb + var dev = e.clientX - this.getBoundingClientRect().left - thumb.width / 2 - + (value - min) * multiplier; + // if click was not on thumb, move thumb to click location + if (Math.abs(dev) > thumb.radius) { + isChanged = true; + this.value -= -dev / multiplier; + } + rawValue = value; + prevX = e.clientX; + this.addEventListener('mousemove', onDrag, true); + this.addEventListener('mouseup', onDragEnd, true); + } + + function onDrag(e) { + var width = parseFloat(getComputedStyle(this, 0).width); + var multiplier = (width - thumb.width) / range; + if (!multiplier) + return; + rawValue += (e.clientX - prevX) / multiplier; + prevX = e.clientX; + isChanged = true; + this.value = rawValue; + } + + function onDragEnd() { + this.removeEventListener('mousemove', onDrag, true); + this.removeEventListener('mouseup', onDragEnd, true); + } + + function onKeyDown(e) { + if (e.keyCode > 36 && e.keyCode < 41) { // 37-40: left, up, right, down + onFocus.call(this); + isChanged = true; + this.value = value + (e.keyCode == 38 || e.keyCode == 39 ? step : -step); + } + } + + function onFocus() { + if (!isClick) + this.style.boxShadow = !isMac ? '0 0 0 2px #fb0' : + 'inset 0 0 20px rgba(0,127,255,.1), 0 0 1px rgba(0,127,255,.4)'; + } + + function onBlur() { + this.style.boxShadow = ''; + } + + // determines whether value is valid number in attribute form + function isAttrNum(value) { + return !isNaN(value) && +value == parseFloat(value); + } + + // validates min, max, and step attributes and redraws + function update() { + min = isAttrNum(slider.min) ? +slider.min : 0; + max = isAttrNum(slider.max) ? +slider.max : 100; + if (max < min) + max = min > 100 ? min : 100; + step = isAttrNum(slider.step) && slider.step > 0 ? +slider.step : 1; + range = max - min; + draw(true); + } + + // recalculates value property + function calc() { + if (!isValueSet && !areAttrsSet) + value = slider.getAttribute('value'); + if (!isAttrNum(value)) + value = (min + max) / 2; + ; + // snap to step intervals (WebKit sometimes does not - bug?) + value = Math.round((value - min) / step) * step + min; + if (value < min) + value = min; + else if (value > max) + value = min + ~~(range / step) * step; + } + + // renders slider using CSS background ;) + function draw(attrsModified) { + calc(); + if (isChanged && value != prevValue) + slider.dispatchEvent(onChange); + isChanged = false; + if (!attrsModified && value == prevValue) + return; + prevValue = value; + var position = range ? (value - min) / range * 100 : 0; + var bg = '-moz-element(#__sliderthumb__) ' + position + '% no-repeat, '; + style(slider, { background: bg + track }); + } + + } + + function style(element, styles) { + for (var prop in styles) + element.style.setProperty(prop, styles[prop], 'important'); + } + +})(); \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 0824b54..a0e2116 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,6 +9,7 @@ {% compress css %} + @@ -57,6 +58,7 @@ + diff --git a/templates/views/HeaderView.html b/templates/views/HeaderView.html index 08d78af..ce09f10 100644 --- a/templates/views/HeaderView.html +++ b/templates/views/HeaderView.html @@ -34,6 +34,9 @@ {% endif %} + + Listen Live + {% if user.is_authenticated %} Upload diff --git a/templates/views/NowPlayingView.html b/templates/views/NowPlayingView.html index 7d12a82..d87b153 100644 --- a/templates/views/NowPlayingView.html +++ b/templates/views/NowPlayingView.html @@ -2,7 +2,7 @@
-