From f3f5bbb62a4134c6787375bff918200c49ed9853 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Mon, 17 Jun 2013 12:16:12 +0100 Subject: [PATCH] Fixed player not re-initializing --- manage.py | 0 spa/api/v1/UserResource.py | 12 +- spa/models/userprofile.py | 6 + static/css/deepsouthsounds.css | 7 +- static/js/app/appv2.coffee | 2 - static/js/app/appv2.js | 8 +- static/js/app/dss.bootstrapper.js | 1 + .../app/lib/backbone.dss.model.collection.js | 3 +- static/js/app/lib/backbone.dss.model.js | 2 +- .../js/app/models/user/userCollection.coffee | 36 ++ static/js/app/models/user/userCollection.js | 40 +++ static/js/app/views/header.js | 12 +- static/js/app/views/mix/mixItemView.coffee | 9 +- static/js/app/views/mix/mixItemView.js | 31 +- static/js/app/views/user/userListView.coffee | 16 +- static/js/app/views/user/userListView.js | 25 +- static/js/com.podnoms.player.js | 3 + static/js/libs/backbone/backbone.mine.js | 58 --- static/js/libs/bootstrap/bootpag.js | 19 + .../js/libs/bootstrap/bootstrap.datagrid.js | 336 ++++++++++++++++++ static/js/libs/jquery.dataTables.bootstrap.js | 146 ++++++++ templates/javascript/settings.js | 14 +- templates/views/UserListView.html | 35 +- templates/views/scratchpad.html | 15 + 24 files changed, 698 insertions(+), 138 deletions(-) mode change 100755 => 100644 manage.py delete mode 100644 static/js/libs/backbone/backbone.mine.js create mode 100644 static/js/libs/bootstrap/bootpag.js create mode 100644 static/js/libs/bootstrap/bootstrap.datagrid.js create mode 100644 static/js/libs/jquery.dataTables.bootstrap.js create mode 100644 templates/views/scratchpad.html diff --git a/manage.py b/manage.py old mode 100755 new mode 100644 diff --git a/spa/api/v1/UserResource.py b/spa/api/v1/UserResource.py index 2562cc5..802327d 100644 --- a/spa/api/v1/UserResource.py +++ b/spa/api/v1/UserResource.py @@ -1,5 +1,6 @@ from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +from django.db.models import Count import humanize from tastypie import fields from tastypie.authentication import Authentication @@ -15,7 +16,7 @@ from spa.models import UserProfile, Mix class UserProfileResource(BackboneCompatibleResource): class Meta: - queryset = UserProfile.objects.all() + queryset = UserProfile.manager.annotate(fcount=Count('followers')) resource_name = 'profile' include_resource_uri = False include_absolute_url = False @@ -102,7 +103,8 @@ class UserResource(BackboneCompatibleResource): def prepend_urls(self): return [ - url(r"^(?P%s)/(?P[\w\d_.-]+)/favourites%s$" % (self._meta.resource_name, trailing_slash()), + url(r"^(?P%s)/(?P[\w\d_.-]+)/favourites%s$" % ( + self._meta.resource_name, trailing_slash()), self.wrap_view('get_user_favourites'), name="api_get_user_favourites"), url(r"^(?P%s)/(?P\d+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), @@ -110,9 +112,13 @@ class UserResource(BackboneCompatibleResource): self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), ] + def get_object_list(self, request): + return super(UserResource, self).get_object_list(request) + def get_user_favourites(self, request, **kwargs): try: - obj = self.cached_obj_get(bundle=self.build_bundle(request=request), **self.remove_api_resource_names(kwargs)) + obj = self.cached_obj_get(bundle=self.build_bundle(request=request), + **self.remove_api_resource_names(kwargs)) except ObjectDoesNotExist: return HttpGone() except MultipleObjectsReturned: diff --git a/spa/models/userprofile.py b/spa/models/userprofile.py index 3349277..0bf1e42 100644 --- a/spa/models/userprofile.py +++ b/spa/models/userprofile.py @@ -21,10 +21,16 @@ def avatar_name(instance, filename): return generate_save_file_name(str(instance.id), 'avatars', filename) +class UserProfileManager(models.Manager): + def get_query_set(self): + return super(UserProfileManager, self).get_query_set().annotate(fcount=models.Count('followers')) + + class UserProfile(_BaseModel): class Meta: app_label = 'spa' + manager = UserProfileManager() ACTIVITY_SHARE_LIKES = 1 ACTIVITY_SHARE_FAVOURITES = 2 ACTIVITY_SHARE_COMMENTS = 4 diff --git a/static/css/deepsouthsounds.css b/static/css/deepsouthsounds.css index 606c859..ef061e4 100644 --- a/static/css/deepsouthsounds.css +++ b/static/css/deepsouthsounds.css @@ -16,7 +16,7 @@ body { padding-top: 45px; padding-bottom: 40px; padding-right: 32px; - background: url('../img/bg.gif'); + /*background: url('../img/bg.gif');*/ } /* IE/Chrome image fix */ @@ -480,4 +480,9 @@ div.event-content td { width: 98%; text-align: center; margin-bottom: 8px; +} +dss-datatable{ + cellpadding: 0; + cellspacing: 0; + border: 0; } \ No newline at end of file diff --git a/static/js/app/appv2.coffee b/static/js/app/appv2.coffee index e51a499..cf3fb9c 100644 --- a/static/js/app/appv2.coffee +++ b/static/js/app/appv2.coffee @@ -65,8 +65,6 @@ define ['backbone', 'marionette', 'app.lib/router', 'app.lib/panningRegion', 'ap ) true - console.warn("Creating event aggregator shim") - window._eventAggregator = _.extend({}, Backbone.Events); App.headerRegion.show(new HeaderView()); sidebarView = new SidebarView(); App.sidebarRegion.show(sidebarView) diff --git a/static/js/app/appv2.js b/static/js/app/appv2.js index 9b8413b..8264c1e 100644 --- a/static/js/app/appv2.js +++ b/static/js/app/appv2.js @@ -1,8 +1,8 @@ -// Generated by CoffeeScript 1.6.2 +// Generated by CoffeeScript 1.3.3 (function() { + define(['backbone', 'marionette', 'app.lib/router', 'app.lib/panningRegion', 'app.lib/audioController', 'views/header', 'views/sidebar/sidebarView', 'models/mix/mixCollection'], function(Backbone, Marionette, DssRouter, PanningRegion, AudioController, HeaderView, SidebarView, MixCollection) { var App, sidebarView; - Marionette.Region.prototype.open = function(view) { this.$el.hide(); this.$el.html(view.el); @@ -36,7 +36,6 @@ }); App.vent.on("routing:started", function() { var enablePushState, pushState; - console.log("App(vent): routing:started"); enablePushState = true; pushState = !!(enablePushState && window.history && window.history.pushState); @@ -62,7 +61,6 @@ console.log("App: gobbling links"); $(document).on("click", "a[href]:not([data-bypass])", function(evt) { var href, root; - href = { prop: $(this).prop("href"), attr: $(this).attr("href") @@ -76,8 +74,6 @@ }); return true; }); - console.warn("Creating event aggregator shim"); - window._eventAggregator = _.extend({}, Backbone.Events); App.headerRegion.show(new HeaderView()); sidebarView = new SidebarView(); App.sidebarRegion.show(sidebarView); diff --git a/static/js/app/dss.bootstrapper.js b/static/js/app/dss.bootstrapper.js index 5ac1c44..2949867 100644 --- a/static/js/app/dss.bootstrapper.js +++ b/static/js/app/dss.bootstrapper.js @@ -10,6 +10,7 @@ requirejs.config({ 'backbone.wreqr': 'libs/backbone/backbone.wreqr', ich: 'libs/ICanHaz', underscore: 'libs/backbone/underscore', + backgrid: 'libs/backgrid/backgrid', text: 'libs/text', templates: '/templates', app: 'app/appv2', diff --git a/static/js/app/lib/backbone.dss.model.collection.js b/static/js/app/lib/backbone.dss.model.collection.js index 2883bea..56d9eba 100644 --- a/static/js/app/lib/backbone.dss.model.collection.js +++ b/static/js/app/lib/backbone.dss.model.collection.js @@ -1,7 +1,8 @@ define(['backbone'], function (Backbone) { return Backbone.Collection.extend({ parse: function (response) { - this.recent_meta = response.meta || {}; + this.meta = response.meta || {}; + this.page_count = this.meta.total_count / this.meta.limit; return response.objects || response; } }); diff --git a/static/js/app/lib/backbone.dss.model.js b/static/js/app/lib/backbone.dss.model.js index b1a4a01..064ddbc 100644 --- a/static/js/app/lib/backbone.dss.model.js +++ b/static/js/app/lib/backbone.dss.model.js @@ -12,7 +12,7 @@ define(['backbone'], function (Backbone) { var TastypieCollection = Backbone.Collection.extend({ parse: function (response) { - this.recent_meta = response.meta || {}; + this.meta = response.meta || {}; return response.objects || response; } }); diff --git a/static/js/app/models/user/userCollection.coffee b/static/js/app/models/user/userCollection.coffee index 5dc61e5..624d0a5 100644 --- a/static/js/app/models/user/userCollection.coffee +++ b/static/js/app/models/user/userCollection.coffee @@ -4,5 +4,41 @@ define ['backbone', 'models/user/userItem', 'app.lib/backbone.dss.model.collecti model: UserItem url:com.podnoms.settings.urlRoot + "user/" + _columns: [ + property: 'toponymName' + label: 'Name' + sortable: true + , + property: 'uploads' + label: 'Uploads' + sortable: true + , + property: 'likes' + label: 'Likes' + sortable: true + , + property: 'favourites' + label: 'Favourites' + sortable: true + , + property: 'followers' + label: 'Followers' + sortable: true + , + property: 'following' + label: 'Following' + sortable: true + , + property: 'lastseen' + label: 'Last seen' + sortable: true + ] + + columns: -> + @_columns + + data: -> + @toJSON + UserCollection diff --git a/static/js/app/models/user/userCollection.js b/static/js/app/models/user/userCollection.js index 29367d8..f1d0269 100644 --- a/static/js/app/models/user/userCollection.js +++ b/static/js/app/models/user/userCollection.js @@ -18,6 +18,46 @@ UserCollection.prototype.url = com.podnoms.settings.urlRoot + "user/"; + UserCollection.prototype._columns = [ + { + property: 'toponymName', + label: 'Name', + sortable: true + }, { + property: 'uploads', + label: 'Uploads', + sortable: true + }, { + property: 'likes', + label: 'Likes', + sortable: true + }, { + property: 'favourites', + label: 'Favourites', + sortable: true + }, { + property: 'followers', + label: 'Followers', + sortable: true + }, { + property: 'following', + label: 'Following', + sortable: true + }, { + property: 'lastseen', + label: 'Last seen', + sortable: true + } + ]; + + UserCollection.prototype.columns = function() { + return this._columns; + }; + + UserCollection.prototype.data = function() { + return this.toJSON; + }; + return UserCollection; })(DssCollection); diff --git a/static/js/app/views/header.js b/static/js/app/views/header.js index b96709a..1183cf1 100644 --- a/static/js/app/views/header.js +++ b/static/js/app/views/header.js @@ -6,8 +6,8 @@ Code provided under the BSD License: */ -define(['underscore', 'backbone', 'text!/tpl/HeaderView'], - function (_, Backbone, Template) { +define(['underscore', 'backbone', 'vent', 'text!/tpl/HeaderView'], + function (_, Backbone, vent, Template) { return Backbone.View.extend({ template: _.template(Template), events: { @@ -17,12 +17,8 @@ define(['underscore', 'backbone', 'text!/tpl/HeaderView'], }, initialize: function () { this.render(); - _.bindAll(this, "trackChanged"); - _.bindAll(this, "trackPlaying"); - _.bindAll(this, "trackPaused"); - window._eventAggregator.bind("track_changed", this.trackChanged); - _eventAggregator.bind("track_playing", this.trackPlaying); - _eventAggregator.bind("track_paused", this.trackPaused); + this.listenTo(vent, 'mix:play', this.trackPlaying); + this.listenTo(vent, 'mix:pause', this.trackPaused); }, login: function () { com.podnoms.utils.modal('tpl/LoginView'); diff --git a/static/js/app/views/mix/mixItemView.coffee b/static/js/app/views/mix/mixItemView.coffee index e00d5cd..54bf4d8 100644 --- a/static/js/app/views/mix/mixItemView.coffee +++ b/static/js/app/views/mix/mixItemView.coffee @@ -34,13 +34,16 @@ define ['moment', 'app', 'vent', 'marionette', 'models/comment/commentCollection totalDurationText = if totalDuration.hours() != 0 then moment(totalDuration).format("HH:mm:ss") else moment(totalDuration).format("mm:ss"); $('#player-duration-' + id, this.el).text(totalDurationText) - #check if we're currently playing - if com.podnoms.player.isPlayingId @model.id - com.podnoms.settings.setupPlayerWrapper @model.get('id') @renderGenres() return + onDomRefresh: -> + #check if we're currently playing + if com.podnoms.player.isPlayingId @model.id + com.podnoms.settings.setupPlayerWrapper @model.get('id'), com.podnoms.player.getStreamUrl(), @el + true + renderGenres: => el = @el $.each @model.get("genre-list"), (data) -> diff --git a/static/js/app/views/mix/mixItemView.js b/static/js/app/views/mix/mixItemView.js index 58a539b..d620f99 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,20 +50,26 @@ 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"); totalDurationText = totalDuration.hours() !== 0 ? moment(totalDuration).format("HH:mm:ss") : moment(totalDuration).format("mm:ss"); $('#player-duration-' + id, this.el).text(totalDurationText); } - if (com.podnoms.player.isPlayingId(this.model.id)) { - com.podnoms.settings.setupPlayerWrapper(this.model.get('id')); - } this.renderGenres(); }; + MixItemView.prototype.onDomRefresh = function() { + if (com.podnoms.player.isPlayingId(this.model.id)) { + com.podnoms.settings.setupPlayerWrapper(this.model.get('id'), com.podnoms.player.getStreamUrl(), this.el); + } + return true; + }; + MixItemView.prototype.renderGenres = function() { var el; + el = this.el; $.each(this.model.get("genre-list"), function(data) { $("#genre-list", el).append('' + this.text + ''); @@ -78,6 +80,7 @@ MixItemView.prototype.renderComments = function() { var comments; + comments = new CommentsCollection(); comments.url = this.model.get("resource_uri") + "comments/"; comments.mix_id = this.model.id; @@ -85,6 +88,7 @@ comments.fetch({ success: function(data) { var content; + console.log(data); content = new CommentsListView({ collection: comments @@ -128,6 +132,7 @@ MixItemView.prototype.mixFavourite = function() { var app; + console.log("MixItemView: favouriteMix"); app = require('app'); app.vent.trigger("mix:favourite", this.model); @@ -136,6 +141,7 @@ MixItemView.prototype.mixLike = function() { var app; + console.log("MixItemView: likeMix"); app = require('app'); app.vent.trigger("mix:like", this.model); @@ -144,6 +150,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/user/userListView.coffee b/static/js/app/views/user/userListView.coffee index e4b3d2a..55ffab8 100644 --- a/static/js/app/views/user/userListView.coffee +++ b/static/js/app/views/user/userListView.coffee @@ -1,26 +1,22 @@ -define ['marionette', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView', 'libs/jquery.dataTables'], +define ['marionette', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView', 'libs/bootstrap/bootpag'], (Marionette, UserCollection, UserItemView, Template) -> class UserListView extends Marionette.CompositeView template: _.template(Template) - tagName: "table" - className: "table table-hover" itemView: UserItemView - - initialize: -> + itemViewContainer: "tbody" + initialize: => console.log "UserListView: initialize" @collection = new UserCollection() @collection.fetch( data: @options success: => console.log("UserListView: Collection fetched") - $(@el).dataTable sDom: "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>" + console.log(@collection) + $("#page-selection").bootpag(total: @collection.page_count).on "page", (event, num) -> # page number here + $("#content").html "Insert content" # some ajax content loading... return ) return - onRender: -> - console.log("UserListView: onRender") - true - UserListView \ No newline at end of file diff --git a/static/js/app/views/user/userListView.js b/static/js/app/views/user/userListView.js index 6a26191..6a098c2 100644 --- a/static/js/app/views/user/userListView.js +++ b/static/js/app/views/user/userListView.js @@ -1,27 +1,26 @@ // Generated by CoffeeScript 1.6.2 (function() { - var __hasProp = {}.hasOwnProperty, + 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', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView', 'libs/jquery.dataTables'], function(Marionette, UserCollection, UserItemView, Template) { + define(['marionette', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView', 'libs/bootstrap/bootpag'], function(Marionette, UserCollection, UserItemView, Template) { var UserListView, _ref; UserListView = (function(_super) { __extends(UserListView, _super); function UserListView() { - _ref = 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); - UserListView.prototype.tagName = "table"; - - UserListView.prototype.className = "table table-hover"; - UserListView.prototype.itemView = UserItemView; + UserListView.prototype.itemViewContainer = "tbody"; + UserListView.prototype.initialize = function() { var _this = this; @@ -31,18 +30,16 @@ data: this.options, success: function() { console.log("UserListView: Collection fetched"); - $(_this.el).dataTable({ - sDom: "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>" + console.log(_this.collection); + $("#page-selection").bootpag({ + total: _this.collection.page_count + }).on("page", function(event, num) { + return $("#content").html("Insert content"); }); } }); }; - UserListView.prototype.onRender = function() { - console.log("UserListView: onRender"); - return true; - }; - return UserListView; })(Marionette.CompositeView); diff --git a/static/js/com.podnoms.player.js b/static/js/com.podnoms.player.js index 3d62531..db92189 100644 --- a/static/js/com.podnoms.player.js +++ b/static/js/com.podnoms.player.js @@ -127,6 +127,9 @@ com.podnoms.player = { isPlayingId: function (id) { return this.isPlaying() && this.currentSound.sID == "com.podnoms.player-" + id; }, + getStreamUrl: function(){ + return this.currentPath; + }, drawTimeline: function (el, boundingEl, duration) { /* Assume 10 markers diff --git a/static/js/libs/backbone/backbone.mine.js b/static/js/libs/backbone/backbone.mine.js deleted file mode 100644 index 385177a..0000000 --- a/static/js/libs/backbone/backbone.mine.js +++ /dev/null @@ -1,58 +0,0 @@ -Backbone.Validate = function (model, changedAttributes) { - - return (function () { - this.errors = {}; - this.attributes = _.clone(model.attributes); - _.extend(this.attributes, changedAttributes); - _.each(model.validates, function (value, rule) { - this.validators[rule](value); - }); - - this.validators = { - required: function (fields) { - _.each(fields, function (field) { - if (_.isEmpty(this.attributes[field]) === true) { - this.addError(field, I18n.t('errors.form.required')); - } - }); - } - }; - - this.addError = function (field, message) { - if (_.isUndefined(this.errors[field])) { - this.errors[field] = []; - } - this.errors[field].push(message); - }; - - return this.errors; - })(); -}; - -window.TastypieModel = Backbone.Model.extend({ - base_url: function () { - var temp_url = Backbone.Model.prototype.url.call(this); - return (temp_url.charAt(temp_url.length - 1) == '/' ? temp_url : temp_url + '/'); - }, - url: function () { - return this.base_url(); - } -}); - -window.TastypieCollection = Backbone.Collection.extend({ - parse: function (response) { - this.recent_meta = response.meta || {}; - return response.objects || response; - } -}); - -window.DSSModel = window.TastypieModel.extend({ - addError: function (field, message) { - if (_.isUndefined(this.errors[field])) { - this.errors[field] = []; - } - this.errors[field].push(message); - return field; - } -}); - diff --git a/static/js/libs/bootstrap/bootpag.js b/static/js/libs/bootstrap/bootpag.js new file mode 100644 index 0000000..77bcf67 --- /dev/null +++ b/static/js/libs/bootstrap/bootpag.js @@ -0,0 +1,19 @@ +/* + + bootpag - jQuery plugin for dynamic pagination + + Copyright (c) 2013 botmonster@7items.com + + Licensed under the MIT license: + http://www.opensource.org/licenses/mit-license.php + + Project home: + http://botmonster.com/jquery-bootpag/ + + Version: 1.0.4 + +*/ +(function(f,q){f.fn.bootpag=function(p){function k(e,b){var c,d=0==a.maxVisible?1:a.maxVisible,n=1==a.maxVisible?0:1,m=Math.floor((b-1)/d)*d,g=e.find("li");a.page=b=0>b?0:b>a.total?a.total:b;g.removeClass("disabled");c=1>b-1?1:a.leaps&&b-1>=a.maxVisible?Math.floor((b-1)/d)*d:b-1;g.first().toggleClass("disabled",1===b).attr("data-lp",c).find("a").attr("href",h(c));n=1==a.maxVisible?0:1;c=b+1>a.total?a.total:a.leaps&&b+1=a.total)return this;!f.isNumeric(a.maxVisible)&&!a.maxVisible&&(a.maxVisible=a.total);l.data("settings",a);return this.each(function(){var e,b,c=f(this),d=['
    '];a.prev&&d.push('");for(b=1;b<=Math.min(a.total,a.maxVisible);b++)d.push('
  • '+b+"
  • ");a.next&&(b=a.leaps&&a.total>a.maxVisible? +Math.min(a.maxVisible+1,a.total):2,d.push('"));d.push("
");c.find("ul.bootpag").remove();c.append(d.join("")).addClass("pagination");e=c.find("ul.bootpag");c.find("li").click(function(){var a=f(this);a.hasClass("disabled")||k(e,parseInt(a.attr("data-lp"),10))});k(e,a.page)})}})(jQuery,window); \ No newline at end of file diff --git a/static/js/libs/bootstrap/bootstrap.datagrid.js b/static/js/libs/bootstrap/bootstrap.datagrid.js new file mode 100644 index 0000000..b225a46 --- /dev/null +++ b/static/js/libs/bootstrap/bootstrap.datagrid.js @@ -0,0 +1,336 @@ +/* + * Fuel UX Datagrid + * https://github.com/ExactTarget/fuelux + * + * Copyright (c) 2012 ExactTarget + * Licensed under the MIT license. + */ + +define(['require','jquery'],function(require) { + + var $ = require('jquery'); + + // Relates to thead .sorted styles in datagrid.less + var SORTED_HEADER_OFFSET = 22; + + + // DATAGRID CONSTRUCTOR AND PROTOTYPE + + var Datagrid = function (element, options) { + this.$element = $(element); + this.$thead = this.$element.find('thead'); + this.$tfoot = this.$element.find('tfoot'); + this.$footer = this.$element.find('tfoot th'); + this.$footerchildren = this.$footer.children().show().css('visibility', 'hidden'); + this.$topheader = this.$element.find('thead th'); + this.$searchcontrol = this.$element.find('.datagrid-search'); + this.$filtercontrol = this.$element.find('.filter'); + this.$pagesize = this.$element.find('.grid-pagesize'); + this.$pageinput = this.$element.find('.grid-pager input'); + this.$pagedropdown = this.$element.find('.grid-pager .dropdown-menu'); + this.$prevpagebtn = this.$element.find('.grid-prevpage'); + this.$nextpagebtn = this.$element.find('.grid-nextpage'); + this.$pageslabel = this.$element.find('.grid-pages'); + this.$countlabel = this.$element.find('.grid-count'); + this.$startlabel = this.$element.find('.grid-start'); + this.$endlabel = this.$element.find('.grid-end'); + + this.$tbody = $('').insertAfter(this.$thead); + this.$colheader = $('').appendTo(this.$thead); + + this.options = $.extend(true, {}, $.fn.datagrid.defaults, options); + + // Shim until v3 -- account for FuelUX select or native select for page size: + if (this.$pagesize.hasClass('select')) { + this.options.dataOptions.pageSize = parseInt(this.$pagesize.select('selectedItem').value, 10); + } else { + this.options.dataOptions.pageSize = parseInt(this.$pagesize.val(), 10); + } + + // Shim until v3 -- account for older search class: + if (this.$searchcontrol.length <= 0) { + this.$searchcontrol = this.$element.find('.search'); + } + + this.columns = this.options.dataSource.columns(); + + this.$nextpagebtn.on('click', $.proxy(this.next, this)); + this.$prevpagebtn.on('click', $.proxy(this.previous, this)); + this.$searchcontrol.on('searched cleared', $.proxy(this.searchChanged, this)); + this.$filtercontrol.on('changed', $.proxy(this.filterChanged, this)); + this.$colheader.on('click', 'th', $.proxy(this.headerClicked, this)); + + if(this.$pagesize.hasClass('select')) { + this.$pagesize.on('changed', $.proxy(this.pagesizeChanged, this)); + } else { + this.$pagesize.on('change', $.proxy(this.pagesizeChanged, this)); + } + + this.$pageinput.on('change', $.proxy(this.pageChanged, this)); + + this.renderColumns(); + + if (this.options.stretchHeight) this.initStretchHeight(); + + this.renderData(); + }; + + Datagrid.prototype = { + + constructor: Datagrid, + + renderColumns: function () { + var self = this; + + this.$footer.attr('colspan', this.columns.length); + this.$topheader.attr('colspan', this.columns.length); + + var colHTML = ''; + + $.each(this.columns, function (index, column) { + colHTML += ''; + }); + + self.$colheader.append(colHTML); + }, + + updateColumns: function ($target, direction) { + this._updateColumns(this.$colheader, $target, direction); + + if (this.$sizingHeader) { + this._updateColumns(this.$sizingHeader, this.$sizingHeader.find('th').eq($target.index()), direction); + } + }, + + _updateColumns: function ($header, $target, direction) { + var className = (direction === 'asc') ? 'icon-chevron-up' : 'icon-chevron-down'; + $header.find('i.datagrid-sort').remove(); + $header.find('th').removeClass('sorted'); + $('').addClass(className + ' datagrid-sort').appendTo($target); + $target.addClass('sorted'); + }, + + updatePageDropdown: function (data) { + var pageHTML = ''; + + for (var i = 1; i <= data.pages; i++) { + pageHTML += '
  • ' + i + '
  • '; + } + + this.$pagedropdown.html(pageHTML); + }, + + updatePageButtons: function (data) { + if (data.page === 1) { + this.$prevpagebtn.attr('disabled', 'disabled'); + } else { + this.$prevpagebtn.removeAttr('disabled'); + } + + if (data.page === data.pages) { + this.$nextpagebtn.attr('disabled', 'disabled'); + } else { + this.$nextpagebtn.removeAttr('disabled'); + } + }, + + renderData: function () { + var self = this; + + this.$tbody.html(this.placeholderRowHTML(this.options.loadingHTML)); + + this.options.dataSource.data(this.options.dataOptions, function (data) { + var itemdesc = (data.count === 1) ? self.options.itemText : self.options.itemsText; + var rowHTML = ''; + + self.$footerchildren.css('visibility', function () { + return (data.count > 0) ? 'visible' : 'hidden'; + }); + + self.$pageinput.val(data.page); + self.$pageslabel.text(data.pages); + self.$countlabel.text(data.count + ' ' + itemdesc); + self.$startlabel.text(data.start); + self.$endlabel.text(data.end); + + self.updatePageDropdown(data); + self.updatePageButtons(data); + + $.each(data.data, function (index, row) { + rowHTML += ''; + $.each(self.columns, function (index, column) { + rowHTML += '' + row[column.property] + ''; + }); + rowHTML += ''; + }); + + if (!rowHTML) rowHTML = self.placeholderRowHTML('0 ' + self.options.itemsText); + + self.$tbody.html(rowHTML); + self.stretchHeight(); + + self.$element.trigger('loaded'); + }); + + }, + + placeholderRowHTML: function (content) { + return '' + content + ''; + }, + + headerClicked: function (e) { + var $target = $(e.target); + if (!$target.hasClass('sortable')) return; + + var direction = this.options.dataOptions.sortDirection; + var sort = this.options.dataOptions.sortProperty; + var property = $target.data('property'); + + if (sort === property) { + this.options.dataOptions.sortDirection = (direction === 'asc') ? 'desc' : 'asc'; + } else { + this.options.dataOptions.sortDirection = 'asc'; + this.options.dataOptions.sortProperty = property; + } + + this.options.dataOptions.pageIndex = 0; + this.updateColumns($target, this.options.dataOptions.sortDirection); + this.renderData(); + }, + + pagesizeChanged: function (e, pageSize) { + if(pageSize) { + this.options.dataOptions.pageSize = parseInt(pageSize.value, 10); + } else { + this.options.dataOptions.pageSize = parseInt($(e.target).val(), 10); + } + + this.options.dataOptions.pageIndex = 0; + this.renderData(); + }, + + pageChanged: function (e) { + var pageRequested = parseInt($(e.target).val(), 10); + pageRequested = (isNaN(pageRequested)) ? 1 : pageRequested; + var maxPages = this.$pageslabel.text(); + + this.options.dataOptions.pageIndex = + (pageRequested > maxPages) ? maxPages - 1 : pageRequested - 1; + + this.renderData(); + }, + + searchChanged: function (e, search) { + this.options.dataOptions.search = search; + this.options.dataOptions.pageIndex = 0; + this.renderData(); + }, + + filterChanged: function (e, filter) { + this.options.dataOptions.filter = filter; + this.options.dataOptions.pageIndex = 0; + this.renderData(); + }, + + previous: function () { + this.options.dataOptions.pageIndex--; + this.renderData(); + }, + + next: function () { + this.options.dataOptions.pageIndex++; + this.renderData(); + }, + + reload: function () { + this.options.dataOptions.pageIndex = 0; + this.renderData(); + }, + + initStretchHeight: function () { + this.$gridContainer = this.$element.parent(); + + this.$element.wrap('
    '); + this.$stretchWrapper = this.$element.parent(); + + this.$headerTable = $('').attr('class', this.$element.attr('class')); + this.$footerTable = this.$headerTable.clone(); + + this.$headerTable.prependTo(this.$gridContainer).addClass('datagrid-stretch-header'); + this.$thead.detach().appendTo(this.$headerTable); + + this.$sizingHeader = this.$thead.clone(); + this.$sizingHeader.find('tr:first').remove(); + + this.$footerTable.appendTo(this.$gridContainer).addClass('datagrid-stretch-footer'); + this.$tfoot.detach().appendTo(this.$footerTable); + }, + + stretchHeight: function () { + if (!this.$gridContainer) return; + + this.setColumnWidths(); + + var targetHeight = this.$gridContainer.height(); + var headerHeight = this.$headerTable.outerHeight(); + var footerHeight = this.$footerTable.outerHeight(); + var overhead = headerHeight + footerHeight; + + this.$stretchWrapper.height(targetHeight - overhead); + }, + + setColumnWidths: function () { + if (!this.$sizingHeader) return; + + this.$element.prepend(this.$sizingHeader); + + var $sizingCells = this.$sizingHeader.find('th'); + var columnCount = $sizingCells.length; + + function matchSizingCellWidth(i, el) { + if (i === columnCount - 1) return; + + var $el = $(el); + var $sourceCell = $sizingCells.eq(i); + var width = $sourceCell.width(); + + // TD needs extra width to match sorted column header + if ($sourceCell.hasClass('sorted') && $el.prop('tagName') === 'TD') width = width + SORTED_HEADER_OFFSET; + + $el.width(width); + } + + this.$colheader.find('th').each(matchSizingCellWidth); + this.$tbody.find('tr:first > td').each(matchSizingCellWidth); + + this.$sizingHeader.detach(); + } + }; + + + // DATAGRID PLUGIN DEFINITION + + $.fn.datagrid = function (option) { + return this.each(function () { + var $this = $(this); + var data = $this.data('datagrid'); + var options = typeof option === 'object' && option; + + if (!data) $this.data('datagrid', (data = new Datagrid(this, options))); + if (typeof option === 'string') data[option](); + }); + }; + + $.fn.datagrid.defaults = { + dataOptions: { pageIndex: 0, pageSize: 10 }, + loadingHTML: '
    ', + itemsText: 'items', + itemText: 'item' + }; + + $.fn.datagrid.Constructor = Datagrid; + +}); \ No newline at end of file diff --git a/static/js/libs/jquery.dataTables.bootstrap.js b/static/js/libs/jquery.dataTables.bootstrap.js new file mode 100644 index 0000000..4fff685 --- /dev/null +++ b/static/js/libs/jquery.dataTables.bootstrap.js @@ -0,0 +1,146 @@ +/* Set the defaults for DataTables initialisation */ +$.extend(true, $.fn.dataTable.defaults, { + "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>", + "sPaginationType": "bootstrap", + "oLanguage": { + "sLengthMenu": "_MENU_ records per page" + } +}); + + +/* Default class modification */ +$.extend($.fn.dataTableExt.oStdClasses, { + "sWrapper": "dataTables_wrapper form-inline" +}); + + +/* API method to get paging information */ +$.fn.dataTableExt.oApi.fnPagingInfo = function (oSettings) { + return { + "iStart": oSettings._iDisplayStart, + "iEnd": oSettings.fnDisplayEnd(), + "iLength": oSettings._iDisplayLength, + "iTotal": oSettings.fnRecordsTotal(), + "iFilteredTotal": oSettings.fnRecordsDisplay(), + "iPage": oSettings._iDisplayLength === -1 ? + 0 : Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength), + "iTotalPages": oSettings._iDisplayLength === -1 ? + 0 : Math.ceil(oSettings.fnRecordsDisplay() / oSettings._iDisplayLength) + }; +}; + + +/* Bootstrap style pagination control */ +$.extend($.fn.dataTableExt.oPagination, { + "bootstrap": { + "fnInit": function (oSettings, nPaging, fnDraw) { + var oLang = oSettings.oLanguage.oPaginate; + var fnClickHandler = function (e) { + e.preventDefault(); + if (oSettings.oApi._fnPageChange(oSettings, e.data.action)) { + fnDraw(oSettings); + } + }; + + $(nPaging).addClass('pagination').append( + '' + ); + var els = $('a', nPaging); + $(els[0]).bind('click.DT', { action: "previous" }, fnClickHandler); + $(els[1]).bind('click.DT', { action: "next" }, fnClickHandler); + }, + + "fnUpdate": function (oSettings, fnDraw) { + var iListLength = 5; + var oPaging = oSettings.oInstance.fnPagingInfo(); + var an = oSettings.aanFeatures.p; + var i, ien, j, sClass, iStart, iEnd, iHalf = Math.floor(iListLength / 2); + + if (oPaging.iTotalPages < iListLength) { + iStart = 1; + iEnd = oPaging.iTotalPages; + } + else if (oPaging.iPage <= iHalf) { + iStart = 1; + iEnd = iListLength; + } else if (oPaging.iPage >= (oPaging.iTotalPages - iHalf)) { + iStart = oPaging.iTotalPages - iListLength + 1; + iEnd = oPaging.iTotalPages; + } else { + iStart = oPaging.iPage - iHalf + 1; + iEnd = iStart + iListLength - 1; + } + + for (i = 0, ien = an.length; i < ien; i++) { + // Remove the middle elements + $('li:gt(0)', an[i]).filter(':not(:last)').remove(); + + // Add the new list items and their event handlers + for (j = iStart; j <= iEnd; j++) { + sClass = (j == oPaging.iPage + 1) ? 'class="active"' : ''; + $('
  • ' + j + '
  • ') + .insertBefore($('li:last', an[i])[0]) + .bind('click', function (e) { + e.preventDefault(); + oSettings._iDisplayStart = (parseInt($('a', this).text(), 10) - 1) * oPaging.iLength; + fnDraw(oSettings); + }); + } + + // Add / remove disabled classes from the static elements + if (oPaging.iPage === 0) { + $('li:first', an[i]).addClass('disabled'); + } else { + $('li:first', an[i]).removeClass('disabled'); + } + + if (oPaging.iPage === oPaging.iTotalPages - 1 || oPaging.iTotalPages === 0) { + $('li:last', an[i]).addClass('disabled'); + } else { + $('li:last', an[i]).removeClass('disabled'); + } + } + } + } +}); + + +/* + * TableTools Bootstrap compatibility + * Required TableTools 2.1+ + */ +if ($.fn.DataTable.TableTools) { + // Set the classes that TableTools uses to something suitable for Bootstrap + $.extend(true, $.fn.DataTable.TableTools.classes, { + "container": "DTTT btn-group", + "buttons": { + "normal": "btn", + "disabled": "disabled" + }, + "collection": { + "container": "DTTT_dropdown dropdown-menu", + "buttons": { + "normal": "", + "disabled": "disabled" + } + }, + "print": { + "info": "DTTT_print_info modal" + }, + "select": { + "row": "active" + } + }); + + // Have the collection use a bootstrap compatible dropdown + $.extend(true, $.fn.DataTable.TableTools.DEFAULTS.oTags, { + "collection": { + "container": "ul", + "button": "li", + "liner": "a" + } + }); +} diff --git a/templates/javascript/settings.js b/templates/javascript/settings.js index 5b93b10..1f7b9da 100644 --- a/templates/javascript/settings.js +++ b/templates/javascript/settings.js @@ -16,15 +16,15 @@ com.podnoms.settings = { urlArgs: {{ IS_DEBUG }} ? "" : "bust="+ (new Date()).getTime(), currentUser: {{ CURRENT_USER_ID }}, /** simple helper to take an api JSON object and initialise a player item */ - setupPlayerWrapper: function (id, stream_url) { + setupPlayerWrapper: function (id, stream_url, el) { com.podnoms.player.setupPlayer({ id: id, - boundingEl: $('#mix-container-' + id), - waveFormEl: $('#waveform-' + id), - playHeadEl: $('#playhead-player-' + id), - loadingEl: $('#progress-player-' + id), - seekHeadEl: $('#player-seekhead'), - playButtonEl: $('#play-pause-button-small-' + id), + boundingEl: $('#mix-container-' + id, el), + waveFormEl: $('#waveform-' + id, el), + playHeadEl: $('#playhead-player-' + id, el), + loadingEl: $('#progress-player-' + id, el), + seekHeadEl: $('#player-seekhead', el), + playButtonEl: $('#play-pause-button-small-' + id, el), url: stream_url || "" }); } diff --git a/templates/views/UserListView.html b/templates/views/UserListView.html index b5d22c7..acf50ec 100644 --- a/templates/views/UserListView.html +++ b/templates/views/UserListView.html @@ -1,12 +1,23 @@ - - - - - - - - - - - - +
    +
    +
    +
    + +
    +
    NameUploadsLikesFavouritesFollowersFollowingLast seen
    + + + + + + + + + + + + +
    NameUploadsLikesFavouritesFollowersFollowingLast seen
    diff --git a/templates/views/scratchpad.html b/templates/views/scratchpad.html new file mode 100644 index 0000000..03f95a3 --- /dev/null +++ b/templates/views/scratchpad.html @@ -0,0 +1,15 @@ +
    Pagination goes here
    + + + + + + + + + + + + + +
    NameUploadsLikesFavouritesFollowersFollowingLast seen