diff --git a/dss/settings.py b/dss/settings.py index 4b24931..51ce5c3 100644 --- a/dss/settings.py +++ b/dss/settings.py @@ -133,7 +133,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'spa.middleware.uploadify.SWFUploadMiddleware', - #'spa.middleware.sqlprinter.SqlPrintingMiddleware', + #'spa.middleware.sqlprinter.SqlPrintingMiddleware' if DEBUG else None, #'debug_toolbar.middleware.DebugToolbarMiddleware', ) diff --git a/spa/admin.py b/spa/admin.py index 166da1b..c6ec94c 100644 --- a/spa/admin.py +++ b/spa/admin.py @@ -7,10 +7,6 @@ from spa.models.release import Release from spa.models.event import Event from spa.models.label import Label from spa.models.mix import Mix -from spa.models.activity import _Activity -from spa.models.mixlike import MixLike -from spa.models.mixplay import MixPlay -from spa.models.mixfavourite import MixFavourite from spa.models.release import ReleaseAudio from spa.models.venue import Venue @@ -22,10 +18,6 @@ class DefaultAdmin(admin.ModelAdmin): admin.site.register(Mix) -admin.site.register(_Activity) -admin.site.register(MixLike) -admin.site.register(MixPlay) -admin.site.register(MixFavourite) admin.site.register(Genre) admin.site.register(Label) admin.site.register(Release, DefaultAdmin) diff --git a/spa/ajax.py b/spa/ajax.py index a3d38c1..a33ef97 100644 --- a/spa/ajax.py +++ b/spa/ajax.py @@ -3,7 +3,6 @@ import logging from django.conf.urls import url from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist from django.db.models import get_model from django.http import HttpResponse @@ -15,10 +14,9 @@ from django.views.decorators.csrf import csrf_exempt from core.utils import live from dss import localsettings, settings from spa import social -from spa.models import UserProfile, mixfavourite, Release +from spa.models import UserProfile, Release, Activity from spa.models.mix import Mix from spa.models.comment import Comment -from spa.models.mixlike import MixLike from core.serialisers import json from core.tasks import create_waveform_task from core.utils.audio.mp3 import mp3_length @@ -160,7 +158,7 @@ def like(request): if mix is not None: if mix.likes.count() == 0: uid = social.post_like(request, mix) - mix.likes.add(MixLike(mix=mix, user=request.user, uid=uid)) + mix.likes.add(Activity(user=request.user)) response = _get_json('Liked') else: for like in mix.likes.all(): @@ -206,7 +204,7 @@ def favourite(request): mix = Mix.objects.get(pk=request.POST['dataId']) if mix is not None: if mix.favourites.count() == 0: - mix.favourites.add(mixfavourite(mix=mix, user=request.user)) + mix.favourites.add(Activity(mix=mix, user=request.user)) response = _get_json('Favourited') else: mix.favourites.all().delete() diff --git a/spa/api/v1/ActivityResource.py b/spa/api/v1/ActivityResource.py index 2557764..4c53d42 100644 --- a/spa/api/v1/ActivityResource.py +++ b/spa/api/v1/ActivityResource.py @@ -3,26 +3,23 @@ from tastypie.authentication import Authentication from tastypie.authorization import Authorization from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource from spa.models import UserProfile -from spa.models.activity import _Activity +from spa.models.activity import Activity class ActivityResource(BackboneCompatibleResource): class Meta: - queryset = _Activity.objects.all().order_by('-date') + queryset = Activity.objects.all().order_by('-date') resource_name = 'activity' authorization = Authorization() authentication = Authentication() always_return_data = True - def get_object_list(self, request): - return self._meta.queryset.select_subclasses() - def dehydrate(self, bundle): try: - if bundle.obj.user is not None and bundle.obj.user.get_profile() is not None: - user_name = bundle.obj.user.get_profile().get_nice_name() - user_image = bundle.obj.user.get_profile().get_small_profile_image() - user_profile = bundle.obj.user.get_profile().get_profile_url() + if bundle.obj.user is not None: + user_name = bundle.obj.user.get_nice_name() + user_image = bundle.obj.user.get_small_profile_image() + user_profile = bundle.obj.user.get_profile_url() else: user_name = "Anonymous" user_image = UserProfile.get_default_avatar_image() diff --git a/spa/api/v1/MixResource.py b/spa/api/v1/MixResource.py index 570b354..4557231 100644 --- a/spa/api/v1/MixResource.py +++ b/spa/api/v1/MixResource.py @@ -5,6 +5,7 @@ from django.template.loader import render_to_string from tastypie import fields from tastypie.authorization import Authorization from tastypie.constants import ALL_WITH_RELATIONS +from tastypie.fields import ToOneField from tastypie.http import HttpGone from tastypie.utils import trailing_slash @@ -22,6 +23,7 @@ class MixResource(BackboneCompatibleResource): class Meta: queryset = Mix.objects.filter(is_active=True) + user = ToOneField('UserResource', 'user') always_return_data = True detail_uri_name = 'slug' excludes = ['download_url', 'is_active', 'local_file', 'upload_date', 'waveform-generated'] diff --git a/spa/api/v1/UserResource.py b/spa/api/v1/UserResource.py index 2ceaa9a..2562cc5 100644 --- a/spa/api/v1/UserResource.py +++ b/spa/api/v1/UserResource.py @@ -1,9 +1,12 @@ from django.contrib.auth.models import User +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned import humanize from tastypie import fields from tastypie.authentication import Authentication from tastypie.authorization import DjangoAuthorization from django.conf.urls import url +from tastypie.http import HttpMultipleChoices, HttpGone +from tastypie.utils import trailing_slash from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource from spa.api.v1.MixResource import MixResource @@ -78,8 +81,8 @@ class UserProfileResource(BackboneCompatibleResource): UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER) bundle.data['mix_count'] = Mix.objects.filter(user=bundle.obj).count() - bundle.data['like_count'] = bundle.obj.likes.count() - bundle.data['favourite_count'] = bundle.obj.favourites.count() + bundle.data['like_count'] = Mix.objects.filter(likes__user=bundle.obj).count() + bundle.data['favourite_count'] = Mix.objects.filter(favourites__user=bundle.obj).count() bundle.data['follower_count'] = bundle.obj.followers.count() bundle.data['following_count'] = bundle.obj.following.count() bundle.data['following'] = bundle.obj.is_follower(bundle.request.user) @@ -95,10 +98,11 @@ class UserResource(BackboneCompatibleResource): excludes = ['is_active', 'is_staff', 'is_superuser', 'password'] authorization = DjangoAuthorization() authentication = Authentication() + favourites = fields.ToManyField(MixResource, 'favourites') def prepend_urls(self): return [ - url(r"^(?P%s)/(?P[\w\d_.-]+)/favourites/$" % self._meta.resource_name, + 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"), @@ -108,12 +112,14 @@ class UserResource(BackboneCompatibleResource): def get_user_favourites(self, request, **kwargs): try: - basic_bundle = self.build_bundle(request=request) - user = self.cached_obj_get(bundle=basic_bundle, **self.remove_api_resource_names(kwargs)) - mixes = MixResource() - return mixes.get_object_list(request).filter() - except Exception, ex: - self.logger.error("Error getting user favourites: %s" % ex.message) + obj = self.cached_obj_get(bundle=self.build_bundle(request=request), **self.remove_api_resource_names(kwargs)) + except ObjectDoesNotExist: + return HttpGone() + except MultipleObjectsReturned: + return HttpMultipleChoices("More than one resource is found at this URI.") + + mixes = MixResource() + return mixes.get_list(request, favourites__user=obj.get_profile()) def dehydrate(self, bundle): if bundle.obj.id != bundle.request.user.id: diff --git a/spa/management/commands/importdownloads.py b/spa/management/commands/importdownloads.py new file mode 100644 index 0000000..4ee0e29 --- /dev/null +++ b/spa/management/commands/importdownloads.py @@ -0,0 +1,36 @@ +from django.core.exceptions import ObjectDoesNotExist +from django.core.management.base import NoArgsCommand +import csv +from spa.models import Mix, Activity, UserProfile + + +class Command(NoArgsCommand): + help = "Import activity post activity refactor" + + def handle_noargs(self, **options): + print "Importing from downloads.txt" + f = open("D:\\Working\\Dropbox\\Private\\deepsouthsounds.com\\dss\\spa\\management\\commands\\downloads.txt", "rt") + rows = csv.DictReader(f, dialect='excel-tab') + for row in rows: + try: + mix = Mix.objects.get(id=row['mix_id']) + user = None + + if row['user_id'] != '': + try: + user = UserProfile.objects.get(user__id=row['user_id']) + except ObjectDoesNotExist: + pass + activity = Activity(user=user, date=row['date'], activity_type='p') + activity.save() + + mix.downloads.add(activity) + mix.save() + print "Added download: %s" % mix.title + except ObjectDoesNotExist: + pass + except Exception, e: + print "Error: %s" % e.message + + + diff --git a/spa/management/commands/importplays.py b/spa/management/commands/importplays.py new file mode 100644 index 0000000..2af3894 --- /dev/null +++ b/spa/management/commands/importplays.py @@ -0,0 +1,36 @@ +from django.core.exceptions import ObjectDoesNotExist +from django.core.management.base import NoArgsCommand +import csv +from spa.models import Mix, Activity, UserProfile + + +class Command(NoArgsCommand): + help = "Import activity post activity refactor" + + def handle_noargs(self, **options): + print "Importing from plays.txt" + f = open("D:\\Working\\Dropbox\\Private\\deepsouthsounds.com\\dss\\spa\\management\\commands\\plays.txt", "rt") + rows = csv.DictReader(f, dialect='excel-tab') + for row in rows: + try: + mix = Mix.objects.get(id=row['mix_id']) + user = None + + if row['user_id'] != '': + try: + user = UserProfile.objects.get(user__id=row['user_id']) + except ObjectDoesNotExist: + pass + activity = Activity(user=user, date=row['date'], activity_type='p') + activity.save() + + mix.plays.add(activity) + mix.save() + print "Added play: %s" % mix.title + except ObjectDoesNotExist: + pass + except Exception, e: + print "Error: %s" % e.message + + + diff --git a/spa/models/__init__.py b/spa/models/__init__.py index c9e8565..915982c 100644 --- a/spa/models/__init__.py +++ b/spa/models/__init__.py @@ -7,11 +7,7 @@ from venue import Venue from event import Event from label import Label from mix import Mix -from activity import _Activity -from mixlike import MixLike -from mixplay import MixPlay -from mixfavourite import MixFavourite -from mixdownload import MixDownload +from activity import Activity from genre import Genre from tracklist import Tracklist from purchaselink import PurchaseLink diff --git a/spa/models/_basemodel.py b/spa/models/_basemodel.py index a905296..773cf6a 100644 --- a/spa/models/_basemodel.py +++ b/spa/models/_basemodel.py @@ -14,7 +14,7 @@ class _BaseModel(models.Model): app_label = 'spa' def tosimplejson(self): - ret = simplejson.dump(self) + return simplejson.dump(self) @classmethod def get_lookup(cls, filter_field, transform=None, filter=None): diff --git a/spa/models/activity.py b/spa/models/activity.py index aba70cf..5bf2a1c 100644 --- a/spa/models/activity.py +++ b/spa/models/activity.py @@ -1,49 +1,33 @@ -import abc - -from django.contrib.auth.models import User from django.db import models - -from model_utils.managers import InheritanceManager - +from spa.models.userprofile import UserProfile from spa.models._basemodel import _BaseModel -from spa.models.managers.QueuedActivityModelManager import QueuedActivityModelManager + +ACTIVITYTYPES = ( + ('p', 'played'), + ('d', 'downloaded'), + ('l', 'liked'), + ('f', 'favourited'), +) -class _Activity(_BaseModel): - user = models.ForeignKey(User, null=True, related_name='activity') - uid = models.CharField(max_length=50, blank=True, null=True) +class Activity(_BaseModel): + user = models.ForeignKey(UserProfile, null=True, blank=True) date = models.DateTimeField(auto_now=True) - objects = InheritanceManager() - - message_manager = QueuedActivityModelManager() - - class Meta: - app_label = 'spa' + activity_type = models.CharField(max_length=1, choices=ACTIVITYTYPES) def __unicode__(self): return "%s" % self.date - @abc.abstractmethod def get_verb_passed(self): - return + verb = [item for item in ACTIVITYTYPES if item[0] == self.activity_type][0] + return str(verb[1]) - @abc.abstractmethod - def get_verb_present(self): - return - - @abc.abstractmethod def get_object_singular(self): - return + return "mix" - @abc.abstractmethod - def get_object_plural(self): - return - - @abc.abstractmethod def get_object_name(self): - return + return "mix" - @abc.abstractmethod def get_object_url(self): - return + return "url" diff --git a/spa/models/mix.py b/spa/models/mix.py index 09507d7..459660d 100644 --- a/spa/models/mix.py +++ b/spa/models/mix.py @@ -5,18 +5,14 @@ import urlparse from sorl.thumbnail import get_thumbnail from django.contrib.sites.models import Site from django.db import models -from django.db.models import Count from core.utils import url from core.utils.audio.mp3 import mp3_length from core.utils.url import unique_slugify -from spa.models.mixlike import MixLike +from spa.models.activity import Activity from spa.models.genre import Genre -from spa.models.mixplay import MixPlay -from spa.models.mixdownload import MixDownload from dss import settings, localsettings from spa.models.userprofile import UserProfile from spa.models._basemodel import _BaseModel -from spa.models.mixfavourite import MixFavourite from core.utils.file import generate_save_file_name @@ -55,8 +51,10 @@ class Mix(_BaseModel): genres = models.ManyToManyField(Genre) - class ImmutableMeta: - immutable = ['user'] + favourites = models.ManyToManyField(Activity, related_name='mix_favourites') + likes = models.ManyToManyField(Activity, related_name='mix_likes') + plays = models.ManyToManyField(Activity, related_name='mix_plays') + downloads = models.ManyToManyField(Activity, related_name='mix_downloads') def __unicode__(self): return self.title @@ -137,60 +135,23 @@ class Mix(_BaseModel): .filter(waveform_generated=True) \ .order_by('-id') - @classmethod - def get_listing(cls, listing_type, user=None, queryset=None): - candidates = queryset or Mix.objects \ - .filter(waveform_generated=True) \ - .filter(is_featured=True) \ - .exclude(duration__isnull=True) - - if listing_type == 'latest': - queryset = candidates.order_by('-id') - elif listing_type == 'likes': - queryset = candidates.annotate(karma=Count('likes')).order_by('-karma') - elif listing_type == 'favourites': - queryset = candidates.annotate(karma=Count('likes')).order_by('-karma') - elif listing_type == 'toprated': - queryset = candidates.annotate(karma=Count('likes')).order_by('-karma') - elif listing_type == 'mostactive': - queryset = candidates.filter(waveform_generated=True).annotate(karma=Count('comments')).order_by('-karma') - elif listing_type == 'mostplayed': - queryset = candidates.annotate(karma=Count('plays')).order_by('-karma') - elif listing_type == 'recommended': - queryset = candidates.order_by('-id') - elif listing_type == 'favourites': - queryset = candidates.filter(favourites__user=user).order_by('favourites__date') - else: - #check if we have a valid genre - queryset = candidates.filter(genres__slug__exact=listing_type) - return queryset - - @classmethod - def get_user_mixes(cls, user_name): - mixes = Mix.objects.filter(user__user__username=user_name) - if mixes.count(): - return { - "inline_play": False, - "heading": "Some mixes from " + mixes[0].user.user.get_full_name() or mixes[0].user.user.username, - "latest_mix_list": mixes, - } - - return { - "heading": "No mixes found for this user", - "latest_mix_list": None, - } - def add_download(self, user): try: - self.downloads.add(MixDownload(user=user if user.is_authenticated() else None)) + if user.is_authenticated(): + activity = Activity(user=user.get_profile(), activity_type='d') + activity.save() + self.downloads.add(activity) except Exception, e: - self.logger.exception("Error adding mix download") + self.logger.exception("Error adding mix download: %s" % e.message) def add_play(self, user): try: - self.plays.add(MixPlay(user=user if user.is_authenticated() else None)) + if user.is_authenticated(): + activity = Activity(user=user.get_profile(), activity_type='p') + activity.save() + self.plays.add(activity) except Exception, e: - self.logger.exception("Unable to add mix play") + self.logger.exception("Unable to add mix play: %s" % e.message) def is_liked(self, user): if user is None: @@ -207,7 +168,9 @@ class Mix(_BaseModel): if user.is_authenticated(): if value: if self.favourites.filter(user=user).count() == 0: - self.favourites.add(MixFavourite(mix=self, user=user.get_profile())) + activity = Activity(user=user.get_profile(), activity_type='f') + activity.save() + self.favourites.add(activity) else: self.favourites.filter(user=user).delete() except Exception, ex: @@ -220,7 +183,9 @@ class Mix(_BaseModel): if user.is_authenticated(): if value: if self.likes.filter(user=user).count() == 0: - self.likes.add(MixLike(mix=self, user=user.get_profile())) + activity = Activity(user=user.get_profile(), activity_type='l') + activity.save() + self.likes.add(activity) else: self.likes.filter(user=user).delete() except Exception, ex: diff --git a/spa/models/mixdownload.py b/spa/models/mixdownload.py deleted file mode 100644 index 0338d59..0000000 --- a/spa/models/mixdownload.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.db import models -from spa.models.activity import _Activity - -class MixDownload(_Activity): - mix = models.ForeignKey('spa.Mix', related_name='downloads') - - def get_verb_passed(self): - return "downloaded" - - def get_object_singular(self): - return "mix" - - def get_object_name(self): - return self.mix.title - - def get_object_url(self): - return self.mix.get_absolute_url() \ No newline at end of file diff --git a/spa/models/mixfavourite.py b/spa/models/mixfavourite.py deleted file mode 100644 index 3b6cb05..0000000 --- a/spa/models/mixfavourite.py +++ /dev/null @@ -1,10 +0,0 @@ -from spa.models.userprofile import UserProfile -from spa.models._basemodel import _BaseModel -from django.db import models - - -class MixFavourite(_BaseModel): - mix = models.ForeignKey('spa.Mix', related_name='favourites') - user = models.ForeignKey(UserProfile, related_name='favourites') - date = models.DateTimeField(auto_now=True) - diff --git a/spa/models/mixlike.py b/spa/models/mixlike.py deleted file mode 100644 index 2888c32..0000000 --- a/spa/models/mixlike.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.db import models -from spa.models.userprofile import UserProfile -from spa.models._basemodel import _BaseModel - - -class MixLike(_BaseModel): - mix = models.ForeignKey('spa.Mix', related_name='likes') - user = models.ForeignKey(UserProfile, related_name='likes') - date = models.DateTimeField(auto_now=True) diff --git a/spa/models/mixplay.py b/spa/models/mixplay.py deleted file mode 100644 index 2ed20bf..0000000 --- a/spa/models/mixplay.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.db import models -from spa.models.activity import _Activity - - -class MixPlay(_Activity): - mix = models.ForeignKey('spa.Mix', related_name='plays') - - def __unicode__(self): - if self.user is None: - return "%s %s %s %s" % ("Anonymous", self.get_verb_passed(), self.get_object_name(), self.date) - else: - return "%s %s %s %s" % (self.user.get_full_name(), self.get_verb_passed(), self.get_object_name(), self.date) - - def get_verb_passed(self): - return "played" - - def get_object_singular(self): - return "mix" - - def get_object_name(self): - return self.mix.title - - def get_object_url(self): - return self.mix.get_absolute_url() - diff --git a/spa/signals.py b/spa/signals.py index 98fa050..3149781 100644 --- a/spa/signals.py +++ b/spa/signals.py @@ -2,17 +2,13 @@ from celery.task import task from django.core.exceptions import ObjectDoesNotExist from django.db.models.signals import post_save from django.dispatch import Signal -from kombu import Connection -from kombu.entity import Exchange from django.contrib.auth.models import User from south import signals from core.utils.audio.mp3 import mp3_length -from dss import localsettings, settings -from spa.models import _Activity +from dss import settings from spa.models import UserProfile from spa.models.mix import Mix -import pika waveform_generated = Signal() diff --git a/static/js/app/views/activity/activityListView.coffee b/static/js/app/views/activity/activityListView.coffee index 31b1ea8..d136941 100644 --- a/static/js/app/views/activity/activityListView.coffee +++ b/static/js/app/views/activity/activityListView.coffee @@ -14,6 +14,7 @@ define ['marionette', 'models/activity/activityCollection', 'views/activity/acti @collection.fetch( success: => console.log "ActivityListView: Collection fetched" + console.log @collection return ) return diff --git a/static/js/app/views/activity/activityListView.js b/static/js/app/views/activity/activityListView.js index 9b79122..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,11 +26,13 @@ ActivityListView.prototype.initialize = function() { var _this = this; + console.log("ActivityListView: initialize"); this.collection = new ActivityCollection; this.collection.fetch({ success: function() { console.log("ActivityListView: Collection fetched"); + console.log(_this.collection); } }); }; diff --git a/static/js/app/views/mix/mixDetailView.js b/static/js/app/views/mix/mixDetailView.js index ea3c7f2..1e49474 100644 --- a/static/js/app/views/mix/mixDetailView.js +++ b/static/js/app/views/mix/mixDetailView.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/mix/mixItem', 'views/mix/mixItemView', 'text!/tpl/MixDetailView'], function(Marionette, MixItem, MixItemView, Template) { - var MixDetailView; - MixDetailView = (function(_super) { + var MixDetailView, _ref; + MixDetailView = (function(_super) { __extends(MixDetailView, _super); function MixDetailView() { - return MixDetailView.__super__.constructor.apply(this, arguments); + _ref = MixDetailView.__super__.constructor.apply(this, arguments); + return _ref; } MixDetailView.prototype.template = _.template(Template); @@ -22,6 +23,7 @@ MixDetailView.prototype.onRender = function() { var view; + view = new MixItemView({ tagName: "div", className: "mix-listing audio-listing-single", diff --git a/static/js/app/views/mix/mixEditView.js b/static/js/app/views/mix/mixEditView.js index 9b09598..ec5830b 100644 --- a/static/js/app/views/mix/mixEditView.js +++ b/static/js/app/views/mix/mixEditView.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(['app.lib/editableView', 'moment', 'libs/backbone/backbone.syphon', 'text!/tpl/MixEditView'], function(EditableView, moment, Syphon, Template) { - var MixEditView; - return MixEditView = (function(_super) { + var MixEditView, _ref; + return MixEditView = (function(_super) { __extends(MixEditView, _super); function MixEditView() { - this.saveChanges = __bind(this.saveChanges, this); - return MixEditView.__super__.constructor.apply(this, arguments); + this.saveChanges = __bind(this.saveChanges, this); _ref = MixEditView.__super__.constructor.apply(this, arguments); + return _ref; } MixEditView.prototype.template = _.template(Template); @@ -37,6 +37,7 @@ MixEditView.prototype.onRender = function() { var parent; + console.log("MixEditView: onRender"); this.sendImage = false; parent = this; @@ -78,6 +79,7 @@ }, success: function(data) { var $out; + $out = $("#results"); $out.html("Your results:"); return $out.append("
" + data + "
"); @@ -103,6 +105,7 @@ }, initSelection: function(element, callback) { var genres, result; + result = []; genres = parent.model.get("genre-list"); if (genres !== undefined) { @@ -132,6 +135,7 @@ MixEditView.prototype.saveChanges = function() { var data, _this = this; + console.log("MixEditView: saveChanges"); data = Syphon.serialize(this); this.model.set(data); @@ -186,7 +190,6 @@ MixEditView; - return MixEditView; })(EditableView); diff --git a/static/js/app/views/mix/mixItemView.coffee b/static/js/app/views/mix/mixItemView.coffee index e877046..02f7347 100644 --- a/static/js/app/views/mix/mixItemView.coffee +++ b/static/js/app/views/mix/mixItemView.coffee @@ -81,7 +81,7 @@ define ['moment', 'app', 'marionette', 'models/comment/commentCollection', 'view console.log(data) content = new CommentsListView(collection: comments).render() $("#comments", @el).html content.el - $('#mix-tab a:first', @el).tab('show'); + true true favouriteMix: -> diff --git a/static/js/app/views/mix/mixItemView.js b/static/js/app/views/mix/mixItemView.js index c7da18c..b8759be 100644 --- a/static/js/app/views/mix/mixItemView.js +++ b/static/js/app/views/mix/mixItemView.js @@ -118,7 +118,7 @@ collection: comments }).render(); $("#comments", this.el).html(content.el); - return $('#mix-tab a:first', this.el).tab('show'); + return true; } }); return true; diff --git a/static/js/app/views/mix/mixListView.coffee b/static/js/app/views/mix/mixListView.coffee index ca8b5a7..7259a6a 100644 --- a/static/js/app/views/mix/mixListView.coffee +++ b/static/js/app/views/mix/mixListView.coffee @@ -14,7 +14,8 @@ define ['marionette', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text data: @options success: => console.log("MixListView: Collection fetched") - return + @tabChanged('latest') + true ) return @@ -22,5 +23,10 @@ define ['marionette', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text $('#li-' + @options.type, @el).addClass('active') true + tabChanged: (type) -> + console.log("MixListView: tab changed") + $('#mix-tab a.' + type, @el).tab('show') + true + MixListView diff --git a/static/js/app/views/mix/mixListView.js b/static/js/app/views/mix/mixListView.js index 789048f..257b6a1 100644 --- a/static/js/app/views/mix/mixListView.js +++ b/static/js/app/views/mix/mixListView.js @@ -29,6 +29,8 @@ data: this.options, success: function() { console.log("MixListView: Collection fetched"); + _this.tabChanged('latest'); + return true; } }); }; @@ -38,6 +40,12 @@ return true; }; + MixListView.prototype.tabChanged = function(type) { + console.log("MixListView: tab changed"); + $('#mix-tab a.' + type, this.el).tab('show'); + return true; + }; + return MixListView; })(Marionette.CompositeView); diff --git a/static/js/libs/backbone/backbone.marionette.js b/static/js/libs/backbone/backbone.marionette.js index eca567d..4380894 100644 --- a/static/js/libs/backbone/backbone.marionette.js +++ b/static/js/libs/backbone/backbone.marionette.js @@ -1,6 +1,6 @@ // MarionetteJS (Backbone.Marionette) // ---------------------------------- -// v1.0.2 +// v1.0.3 // // Copyright (c)2013 Derick Bailey, Muted Solutions, LLC. // Distributed under MIT license @@ -8,6 +8,7 @@ // http://marionettejs.com + /*! * Includes BabySitter * https://github.com/marionettejs/backbone.babysitter/ @@ -31,190 +32,188 @@ // Provide a container to store, retrieve and // shut down child views. -Backbone.ChildViewContainer = (function (Backbone, _) { +Backbone.ChildViewContainer = (function(Backbone, _){ + + // Container Constructor + // --------------------- - // Container Constructor - // --------------------- + var Container = function(initialViews){ + this._views = {}; + this._indexByModel = {}; + this._indexByCollection = {}; + this._indexByCustom = {}; + this._updateLength(); - var Container = function (initialViews) { - this._views = {}; - this._indexByModel = {}; - this._indexByCollection = {}; - this._indexByCustom = {}; - this._updateLength(); + this._addInitialViews(initialViews); + }; - this._addInitialViews(initialViews); - }; + // Container Methods + // ----------------- - // Container Methods - // ----------------- + _.extend(Container.prototype, { - _.extend(Container.prototype, { + // Add a view to this container. Stores the view + // by `cid` and makes it searchable by the model + // and/or collection of the view. Optionally specify + // a custom key to store an retrieve the view. + add: function(view, customIndex){ + var viewCid = view.cid; - // Add a view to this container. Stores the view - // by `cid` and makes it searchable by the model - // and/or collection of the view. Optionally specify - // a custom key to store an retrieve the view. - add: function (view, customIndex) { - var viewCid = view.cid; + // store the view + this._views[viewCid] = view; - // store the view - this._views[viewCid] = view; + // index it by model + if (view.model){ + this._indexByModel[view.model.cid] = viewCid; + } - // index it by model - if (view.model) { - this._indexByModel[view.model.cid] = viewCid; - } + // index it by collection + if (view.collection){ + this._indexByCollection[view.collection.cid] = viewCid; + } - // index it by collection - if (view.collection) { - this._indexByCollection[view.collection.cid] = viewCid; - } + // index by custom + if (customIndex){ + this._indexByCustom[customIndex] = viewCid; + } - // index by custom - if (customIndex) { - this._indexByCustom[customIndex] = viewCid; - } + this._updateLength(); + }, - this._updateLength(); - }, + // Find a view by the model that was attached to + // it. Uses the model's `cid` to find it, and + // retrieves the view by it's `cid` from the result + findByModel: function(model){ + var viewCid = this._indexByModel[model.cid]; + return this.findByCid(viewCid); + }, - // Find a view by the model that was attached to - // it. Uses the model's `cid` to find it, and - // retrieves the view by it's `cid` from the result - findByModel: function (model) { - var viewCid = this._indexByModel[model.cid]; - return this.findByCid(viewCid); - }, + // Find a view by the collection that was attached to + // it. Uses the collection's `cid` to find it, and + // retrieves the view by it's `cid` from the result + findByCollection: function(col){ + var viewCid = this._indexByCollection[col.cid]; + return this.findByCid(viewCid); + }, - // Find a view by the collection that was attached to - // it. Uses the collection's `cid` to find it, and - // retrieves the view by it's `cid` from the result - findByCollection: function (col) { - var viewCid = this._indexByCollection[col.cid]; - return this.findByCid(viewCid); - }, + // Find a view by a custom indexer. + findByCustom: function(index){ + var viewCid = this._indexByCustom[index]; + return this.findByCid(viewCid); + }, - // Find a view by a custom indexer. - findByCustom: function (index) { - var viewCid = this._indexByCustom[index]; - return this.findByCid(viewCid); - }, + // Find by index. This is not guaranteed to be a + // stable index. + findByIndex: function(index){ + return _.values(this._views)[index]; + }, - // Find by index. This is not guaranteed to be a - // stable index. - findByIndex: function (index) { - return _.values(this._views)[index]; - }, + // retrieve a view by it's `cid` directly + findByCid: function(cid){ + return this._views[cid]; + }, - // retrieve a view by it's `cid` directly - findByCid: function (cid) { - return this._views[cid]; - }, + // Remove a view + remove: function(view){ + var viewCid = view.cid; - // Remove a view - remove: function (view) { - var viewCid = view.cid; + // delete model index + if (view.model){ + delete this._indexByModel[view.model.cid]; + } - // delete model index - if (view.model) { - delete this._indexByModel[view.model.cid]; - } + // delete collection index + if (view.collection){ + delete this._indexByCollection[view.collection.cid]; + } - // delete collection index - if (view.collection) { - delete this._indexByCollection[view.collection.cid]; - } + // delete custom index + var cust; - // delete custom index - var cust; - - for (var key in this._indexByCustom) { - if (this._indexByCustom.hasOwnProperty(key)) { - if (this._indexByCustom[key] === viewCid) { - cust = key; - break; - } - } - } - - if (cust) { - delete this._indexByCustom[cust]; - } - - // remove the view from the container - delete this._views[viewCid]; - - // update the length - this._updateLength(); - }, - - // Call a method on every view in the container, - // passing parameters to the call method one at a - // time, like `function.call`. - call: function (method, args) { - args = Array.prototype.slice.call(arguments, 1); - this.apply(method, args); - }, - - // Apply a method on every view in the container, - // passing parameters to the call method one at a - // time, like `function.apply`. - apply: function (method, args) { - var view; - - // fix for IE < 9 - args = args || []; - - _.each(this._views, function (view, key) { - if (_.isFunction(view[method])) { - view[method].apply(view, args); - } - }); - - }, - - // Update the `.length` attribute on this container - _updateLength: function () { - this.length = _.size(this._views); - }, - - // set up an initial list of views - _addInitialViews: function (views) { - if (!views) { - return; - } - - var view, i, - length = views.length; - - for (i = 0; i < length; i++) { - view = views[i]; - this.add(view); - } + for (var key in this._indexByCustom){ + if (this._indexByCustom.hasOwnProperty(key)){ + if (this._indexByCustom[key] === viewCid){ + cust = key; + break; + } } - }); + } - // Borrowing this code from Backbone.Collection: - // http://backbonejs.org/docs/backbone.html#section-106 - // - // Mix in methods from Underscore, for iteration, and other - // collection related features. - var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter', - 'select', 'reject', 'every', 'all', 'some', 'any', 'include', - 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest', - 'last', 'without', 'isEmpty', 'pluck']; + if (cust){ + delete this._indexByCustom[cust]; + } - _.each(methods, function (method) { - Container.prototype[method] = function () { - var views = _.values(this._views); - var args = [views].concat(_.toArray(arguments)); - return _[method].apply(_, args); - }; - }); + // remove the view from the container + delete this._views[viewCid]; - // return the public API - return Container; + // update the length + this._updateLength(); + }, + + // Call a method on every view in the container, + // passing parameters to the call method one at a + // time, like `function.call`. + call: function(method, args){ + args = Array.prototype.slice.call(arguments, 1); + this.apply(method, args); + }, + + // Apply a method on every view in the container, + // passing parameters to the call method one at a + // time, like `function.apply`. + apply: function(method, args){ + var view; + + // fix for IE < 9 + args = args || []; + + _.each(this._views, function(view, key){ + if (_.isFunction(view[method])){ + view[method].apply(view, args); + } + }); + + }, + + // Update the `.length` attribute on this container + _updateLength: function(){ + this.length = _.size(this._views); + }, + + // set up an initial list of views + _addInitialViews: function(views){ + if (!views){ return; } + + var view, i, + length = views.length; + + for (i=0; i` blocks, // caching them for faster access. - Marionette.TemplateCache = function (templateId) { - this.templateId = templateId; - }; +Marionette.TemplateCache = function(templateId){ + this.templateId = templateId; +}; // TemplateCache object-level methods. Manage the template -// caches from these method calls instead of creating +// caches from these method calls instead of creating // your own TemplateCache instances - _.extend(Marionette.TemplateCache, { - templateCaches: {}, +_.extend(Marionette.TemplateCache, { + templateCaches: {}, - // Get the specified template by id. Either - // retrieves the cached version, or loads it - // from the DOM. - get: function (templateId) { - var cachedTemplate = this.templateCaches[templateId]; + // Get the specified template by id. Either + // retrieves the cached version, or loads it + // from the DOM. + get: function(templateId){ + var cachedTemplate = this.templateCaches[templateId]; - if (!cachedTemplate) { - cachedTemplate = new Marionette.TemplateCache(templateId); - this.templateCaches[templateId] = cachedTemplate; - } + if (!cachedTemplate){ + cachedTemplate = new Marionette.TemplateCache(templateId); + this.templateCaches[templateId] = cachedTemplate; + } - return cachedTemplate.load(); - }, + return cachedTemplate.load(); + }, - // Clear templates from the cache. If no arguments - // are specified, clears all templates: - // `clear()` - // - // If arguments are specified, clears each of the - // specified templates from the cache: - // `clear("#t1", "#t2", "...")` - clear: function () { - var i; - var args = slice(arguments); - var length = args.length; + // Clear templates from the cache. If no arguments + // are specified, clears all templates: + // `clear()` + // + // If arguments are specified, clears each of the + // specified templates from the cache: + // `clear("#t1", "#t2", "...")` + clear: function(){ + var i; + var args = slice(arguments); + var length = args.length; - if (length > 0) { - for (i = 0; i < length; i++) { - delete this.templateCaches[args[i]]; - } - } else { - this.templateCaches = {}; - } - } - }); + if (length > 0){ + for(i=0; i 0) { - this.showCollection(); - } else { - this.showEmptyView(); - } - }, - - // Internal method to loop through each item in the - // collection view and show it - showCollection: function () { - var ItemView; - this.collection.each(function (item, index) { - ItemView = this.getItemView(item); - this.addItemView(item, ItemView, index); - }, this); - }, - - // Internal method to show an empty view in place of - // a collection of item views, when the collection is - // empty - showEmptyView: function () { - var EmptyView = Marionette.getOption(this, "emptyView"); - - if (EmptyView && !this._showingEmptyView) { - this._showingEmptyView = true; - var model = new Backbone.Model(); - this.addItemView(model, EmptyView, 0); - } - }, - - // Internal method to close an existing emptyView instance - // if one exists. Called when a collection view has been - // rendered empty, and then an item is added to the collection. - closeEmptyView: function () { - if (this._showingEmptyView) { - this.closeChildren(); - delete this._showingEmptyView; - } - }, - - // Retrieve the itemView type, either from `this.options.itemView` - // or from the `itemView` in the object definition. The "options" - // takes precedence. - getItemView: function (item) { - var itemView = Marionette.getOption(this, "itemView"); - - if (!itemView) { - throwError("An `itemView` must be specified", "NoItemViewError"); - } - - return itemView; - }, - - // Render the child item's view and add it to the - // HTML for the collection view. - addItemView: function (item, ItemView, index) { - // get the itemViewOptions if any were specified - var itemViewOptions = Marionette.getOption(this, "itemViewOptions"); - if (_.isFunction(itemViewOptions)) { - itemViewOptions = itemViewOptions.call(this, item, index); - } - - // build the view - var view = this.buildItemView(item, ItemView, itemViewOptions); - - // set up the child view event forwarding - this.addChildViewEventForwarding(view); - - // this view is about to be added - this.triggerMethod("before:item:added", view); - - // Store the child view itself so we can properly - // remove and/or close it later - this.children.add(view); - - // Render it and show it - this.renderItemView(view, index); - - // call the "show" method if the collection view - // has already been shown - if (this._isShown) { - Marionette.triggerMethod.call(view, "show"); - } - - // this view was added - this.triggerMethod("after:item:added", view); - }, - - // Set up the child view event forwarding. Uses an "itemview:" - // prefix in front of all forwarded events. - addChildViewEventForwarding: function (view) { - var prefix = Marionette.getOption(this, "itemViewEventPrefix"); - - // Forward all child item view events through the parent, - // prepending "itemview:" to the event name - this.listenTo(view, "all", function () { - var args = slice(arguments); - args[0] = prefix + ":" + args[0]; - args.splice(1, 0, view); - - Marionette.triggerMethod.apply(this, args); - }, this); - }, - - // render the item view - renderItemView: function (view, index) { - view.render(); - this.appendHtml(this, view, index); - }, - - // Build an `itemView` for every model in the collection. - buildItemView: function (item, ItemViewType, itemViewOptions) { - var options = _.extend({model: item}, itemViewOptions); - return new ItemViewType(options); - }, - - // get the child view by item it holds, and remove it - removeItemView: function (item) { - var view = this.children.findByModel(item); - this.removeChildView(view); - this.checkEmpty(); - }, - - // Remove the child view and close it - removeChildView: function (view) { - - // shut down the child view properly, - // including events that the collection has from it - if (view) { - this.stopListening(view); - - // call 'close' or 'remove', depending on which is found - if (view.close) { - view.close(); - } - else if (view.remove) { - view.remove(); - } - - this.children.remove(view); - } - - this.triggerMethod("item:removed", view); - }, - - // helper to show the empty view if the collection is empty - checkEmpty: function () { - // check if we're empty now, and if we are, show the - // empty view - if (!this.collection || this.collection.length === 0) { - this.showEmptyView(); - } - }, - - // Append the HTML to the collection's `el`. - // Override this method to do something other - // then `.append`. - appendHtml: function (collectionView, itemView, index) { - collectionView.$el.append(itemView.el); - }, - - // Internal method to set up the `children` object for - // storing all of the child views - _initChildViewStorage: function () { - this.children = new Backbone.ChildViewContainer(); - }, - - // Handle cleanup and other closing needs for - // the collection of views. - close: function () { - if (this.isClosed) { - return; - } - - this.triggerMethod("collection:before:close"); - this.closeChildren(); - this.triggerMethod("collection:closed"); - - Marionette.View.prototype.close.apply(this, slice(arguments)); - }, - - // Close the child views that this collection view - // is holding on to, if any - closeChildren: function () { - this.children.each(function (child) { - this.removeChildView(child); - }, this); - this.checkEmpty(); - } + // Override from `Marionette.View` to guarantee the `onShow` method + // of child views is called. + onShowCalled: function(){ + this.children.each(function(child){ + Marionette.triggerMethod.call(child, "show"); }); + }, + + // Internal method to trigger the before render callbacks + // and events + triggerBeforeRender: function(){ + this.triggerMethod("before:render", this); + this.triggerMethod("collection:before:render", this); + }, + + // Internal method to trigger the rendered callbacks and + // events + triggerRendered: function(){ + this.triggerMethod("render", this); + this.triggerMethod("collection:rendered", this); + }, + + // Render the collection of items. Override this method to + // provide your own implementation of a render function for + // the collection view. + render: function(){ + this.isClosed = false; + this.triggerBeforeRender(); + this._renderChildren(); + this.triggerRendered(); + return this; + }, + + // Internal method. Separated so that CompositeView can have + // more control over events being triggered, around the rendering + // process + _renderChildren: function(){ + this.closeEmptyView(); + this.closeChildren(); + + if (this.collection && this.collection.length > 0) { + this.showCollection(); + } else { + this.showEmptyView(); + } + }, + + // Internal method to loop through each item in the + // collection view and show it + showCollection: function(){ + var ItemView; + this.collection.each(function(item, index){ + ItemView = this.getItemView(item); + this.addItemView(item, ItemView, index); + }, this); + }, + + // Internal method to show an empty view in place of + // a collection of item views, when the collection is + // empty + showEmptyView: function(){ + var EmptyView = Marionette.getOption(this, "emptyView"); + + if (EmptyView && !this._showingEmptyView){ + this._showingEmptyView = true; + var model = new Backbone.Model(); + this.addItemView(model, EmptyView, 0); + } + }, + + // Internal method to close an existing emptyView instance + // if one exists. Called when a collection view has been + // rendered empty, and then an item is added to the collection. + closeEmptyView: function(){ + if (this._showingEmptyView){ + this.closeChildren(); + delete this._showingEmptyView; + } + }, + + // Retrieve the itemView type, either from `this.options.itemView` + // or from the `itemView` in the object definition. The "options" + // takes precedence. + getItemView: function(item){ + var itemView = Marionette.getOption(this, "itemView"); + + if (!itemView){ + throwError("An `itemView` must be specified", "NoItemViewError"); + } + + return itemView; + }, + + // Render the child item's view and add it to the + // HTML for the collection view. + addItemView: function(item, ItemView, index){ + // get the itemViewOptions if any were specified + var itemViewOptions = Marionette.getOption(this, "itemViewOptions"); + if (_.isFunction(itemViewOptions)){ + itemViewOptions = itemViewOptions.call(this, item, index); + } + + // build the view + var view = this.buildItemView(item, ItemView, itemViewOptions); + + // set up the child view event forwarding + this.addChildViewEventForwarding(view); + + // this view is about to be added + this.triggerMethod("before:item:added", view); + + // Store the child view itself so we can properly + // remove and/or close it later + this.children.add(view); + + // Render it and show it + this.renderItemView(view, index); + + // call the "show" method if the collection view + // has already been shown + if (this._isShown){ + Marionette.triggerMethod.call(view, "show"); + } + + // this view was added + this.triggerMethod("after:item:added", view); + }, + + // Set up the child view event forwarding. Uses an "itemview:" + // prefix in front of all forwarded events. + addChildViewEventForwarding: function(view){ + var prefix = Marionette.getOption(this, "itemViewEventPrefix"); + + // Forward all child item view events through the parent, + // prepending "itemview:" to the event name + this.listenTo(view, "all", function(){ + var args = slice(arguments); + args[0] = prefix + ":" + args[0]; + args.splice(1, 0, view); + + Marionette.triggerMethod.apply(this, args); + }, this); + }, + + // render the item view + renderItemView: function(view, index) { + view.render(); + this.appendHtml(this, view, index); + }, + + // Build an `itemView` for every model in the collection. + buildItemView: function(item, ItemViewType, itemViewOptions){ + var options = _.extend({model: item}, itemViewOptions); + return new ItemViewType(options); + }, + + // get the child view by item it holds, and remove it + removeItemView: function(item){ + var view = this.children.findByModel(item); + this.removeChildView(view); + this.checkEmpty(); + }, + + // Remove the child view and close it + removeChildView: function(view){ + + // shut down the child view properly, + // including events that the collection has from it + if (view){ + this.stopListening(view); + + // call 'close' or 'remove', depending on which is found + if (view.close) { view.close(); } + else if (view.remove) { view.remove(); } + + this.children.remove(view); + } + + this.triggerMethod("item:removed", view); + }, + + // helper to show the empty view if the collection is empty + checkEmpty: function() { + // check if we're empty now, and if we are, show the + // empty view + if (!this.collection || this.collection.length === 0){ + this.showEmptyView(); + } + }, + + // Append the HTML to the collection's `el`. + // Override this method to do something other + // then `.append`. + appendHtml: function(collectionView, itemView, index){ + collectionView.$el.append(itemView.el); + }, + + // Internal method to set up the `children` object for + // storing all of the child views + _initChildViewStorage: function(){ + this.children = new Backbone.ChildViewContainer(); + }, + + // Handle cleanup and other closing needs for + // the collection of views. + close: function(){ + if (this.isClosed){ return; } + + this.triggerMethod("collection:before:close"); + this.closeChildren(); + this.triggerMethod("collection:closed"); + + Marionette.View.prototype.close.apply(this, slice(arguments)); + }, + + // Close the child views that this collection view + // is holding on to, if any + closeChildren: function(){ + this.children.each(function(child){ + this.removeChildView(child); + }, this); + this.checkEmpty(); + } +}); // Composite View @@ -1762,134 +1748,129 @@ var Marionette = (function (global, Backbone, _) { // Used for rendering a branch-leaf, hierarchical structure. // Extends directly from CollectionView and also renders an // an item view as `modelView`, for the top leaf - Marionette.CompositeView = Marionette.CollectionView.extend({ - constructor: function (options) { - Marionette.CollectionView.apply(this, slice(arguments)); +Marionette.CompositeView = Marionette.CollectionView.extend({ - this.itemView = this.getItemView(); - }, + // Configured the initial events that the composite view + // binds to. Override this method to prevent the initial + // events, or to add your own initial events. + _initialEvents: function(){ + if (this.collection){ + this.listenTo(this.collection, "add", this.addChildView, this); + this.listenTo(this.collection, "remove", this.removeItemView, this); + this.listenTo(this.collection, "reset", this._renderChildren, this); + } + }, - // Configured the initial events that the composite view - // binds to. Override this method to prevent the initial - // events, or to add your own initial events. - _initialEvents: function () { - if (this.collection) { - this.listenTo(this.collection, "add", this.addChildView, this); - this.listenTo(this.collection, "remove", this.removeItemView, this); - this.listenTo(this.collection, "reset", this._renderChildren, this); - } - }, + // Retrieve the `itemView` to be used when rendering each of + // the items in the collection. The default is to return + // `this.itemView` or Marionette.CompositeView if no `itemView` + // has been defined + getItemView: function(item){ + var itemView = Marionette.getOption(this, "itemView") || this.constructor; - // Retrieve the `itemView` to be used when rendering each of - // the items in the collection. The default is to return - // `this.itemView` or Marionette.CompositeView if no `itemView` - // has been defined - getItemView: function (item) { - var itemView = Marionette.getOption(this, "itemView") || this.constructor; + if (!itemView){ + throwError("An `itemView` must be specified", "NoItemViewError"); + } - if (!itemView) { - throwError("An `itemView` must be specified", "NoItemViewError"); - } + return itemView; + }, - return itemView; - }, + // Serialize the collection for the view. + // You can override the `serializeData` method in your own view + // definition, to provide custom serialization for your view's data. + serializeData: function(){ + var data = {}; - // Serialize the collection for the view. - // You can override the `serializeData` method in your own view - // definition, to provide custom serialization for your view's data. - serializeData: function () { - var data = {}; + if (this.model){ + data = this.model.toJSON(); + } - if (this.model) { - data = this.model.toJSON(); - } + return data; + }, - return data; - }, + // Renders the model once, and the collection once. Calling + // this again will tell the model's view to re-render itself + // but the collection will not re-render. + render: function(){ + this.isRendered = true; + this.isClosed = false; + this.resetItemViewContainer(); - // Renders the model once, and the collection once. Calling - // this again will tell the model's view to re-render itself - // but the collection will not re-render. - render: function () { - this.isRendered = true; - this.isClosed = false; - this.resetItemViewContainer(); + this.triggerBeforeRender(); + var html = this.renderModel(); + this.$el.html(html); + // the ui bindings is done here and not at the end of render since they + // will not be available until after the model is rendered, but should be + // available before the collection is rendered. + this.bindUIElements(); + this.triggerMethod("composite:model:rendered"); - this.triggerBeforeRender(); - var html = this.renderModel(); - this.$el.html(html); - // the ui bindings is done here and not at the end of render since they - // will not be available until after the model is rendered, but should be - // available before the collection is rendered. - this.bindUIElements(); - this.triggerMethod("composite:model:rendered"); + this._renderChildren(); - this._renderChildren(); + this.triggerMethod("composite:rendered"); + this.triggerRendered(); + return this; + }, - this.triggerMethod("composite:rendered"); - this.triggerRendered(); - return this; - }, + _renderChildren: function(){ + if (this.isRendered){ + Marionette.CollectionView.prototype._renderChildren.call(this); + this.triggerMethod("composite:collection:rendered"); + } + }, - _renderChildren: function () { - if (this.isRendered) { - Marionette.CollectionView.prototype._renderChildren.call(this); - this.triggerMethod("composite:collection:rendered"); - } - }, + // Render an individual model, if we have one, as + // part of a composite view (branch / leaf). For example: + // a treeview. + renderModel: function(){ + var data = {}; + data = this.serializeData(); + data = this.mixinTemplateHelpers(data); - // Render an individual model, if we have one, as - // part of a composite view (branch / leaf). For example: - // a treeview. - renderModel: function () { - var data = {}; - data = this.serializeData(); - data = this.mixinTemplateHelpers(data); + var template = this.getTemplate(); + return Marionette.Renderer.render(template, data); + }, - var template = this.getTemplate(); - return Marionette.Renderer.render(template, data); - }, + // Appends the `el` of itemView instances to the specified + // `itemViewContainer` (a jQuery selector). Override this method to + // provide custom logic of how the child item view instances have their + // HTML appended to the composite view instance. + appendHtml: function(cv, iv, index){ + var $container = this.getItemViewContainer(cv); + $container.append(iv.el); + }, - // Appends the `el` of itemView instances to the specified - // `itemViewContainer` (a jQuery selector). Override this method to - // provide custom logic of how the child item view instances have their - // HTML appended to the composite view instance. - appendHtml: function (cv, iv) { - var $container = this.getItemViewContainer(cv); - $container.append(iv.el); - }, + // Internal method to ensure an `$itemViewContainer` exists, for the + // `appendHtml` method to use. + getItemViewContainer: function(containerView){ + if ("$itemViewContainer" in containerView){ + return containerView.$itemViewContainer; + } - // Internal method to ensure an `$itemViewContainer` exists, for the - // `appendHtml` method to use. - getItemViewContainer: function (containerView) { - if ("$itemViewContainer" in containerView) { - return containerView.$itemViewContainer; - } + var container; + if (containerView.itemViewContainer){ - var container; - if (containerView.itemViewContainer) { + var selector = _.result(containerView, "itemViewContainer"); + container = containerView.$(selector); + if (container.length <= 0) { + throwError("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer, "ItemViewContainerMissingError"); + } - var selector = _.result(containerView, "itemViewContainer"); - container = containerView.$(selector); - if (container.length <= 0) { - throwError("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer, "ItemViewContainerMissingError"); - } + } else { + container = containerView.$el; + } - } else { - container = containerView.$el; - } + containerView.$itemViewContainer = container; + return container; + }, - containerView.$itemViewContainer = container; - return container; - }, - - // Internal method to reset the `$itemViewContainer` on render - resetItemViewContainer: function () { - if (this.$itemViewContainer) { - delete this.$itemViewContainer; - } - } - }); + // Internal method to reset the `$itemViewContainer` on render + resetItemViewContainer: function(){ + if (this.$itemViewContainer){ + delete this.$itemViewContainer; + } + } +}); // Layout @@ -1901,127 +1882,123 @@ var Marionette = (function (global, Backbone, _) { // A specialized view type that renders an area of HTML and then // attaches `Region` instances to the specified `regions`. // Used for composite view management and sub-application areas. - Marionette.Layout = Marionette.ItemView.extend({ - regionType: Marionette.Region, +Marionette.Layout = Marionette.ItemView.extend({ + regionType: Marionette.Region, + + // Ensure the regions are available when the `initialize` method + // is called. + constructor: function (options) { + options = options || {}; - // Ensure the regions are available when the `initialize` method - // is called. - constructor: function (options) { - options = options || {}; + this._firstRender = true; + this._initializeRegions(options); + + Marionette.ItemView.call(this, options); + }, - this._firstRender = true; - this._initializeRegions(options); + // Layout's render will use the existing region objects the + // first time it is called. Subsequent calls will close the + // views that the regions are showing and then reset the `el` + // for the regions to the newly rendered DOM elements. + render: function(){ - Marionette.ItemView.call(this, options); - }, + if (this._firstRender){ + // if this is the first render, don't do anything to + // reset the regions + this._firstRender = false; + } else if (this.isClosed){ + // a previously closed layout means we need to + // completely re-initialize the regions + this._initializeRegions(); + } else { + // If this is not the first render call, then we need to + // re-initializing the `el` for each region + this._reInitializeRegions(); + } - // Layout's render will use the existing region objects the - // first time it is called. Subsequent calls will close the - // views that the regions are showing and then reset the `el` - // for the regions to the newly rendered DOM elements. - render: function () { + var args = Array.prototype.slice.apply(arguments); + var result = Marionette.ItemView.prototype.render.apply(this, args); - if (this._firstRender) { - // if this is the first render, don't do anything to - // reset the regions - this._firstRender = false; - } else if (this.isClosed) { - // a previously closed layout means we need to - // completely re-initialize the regions - this._initializeRegions(); - } else { - // If this is not the first render call, then we need to - // re-initializing the `el` for each region - this._reInitializeRegions(); - } + return result; + }, - var args = Array.prototype.slice.apply(arguments); - var result = Marionette.ItemView.prototype.render.apply(this, args); + // Handle closing regions, and then close the view itself. + close: function () { + if (this.isClosed){ return; } + this.regionManager.close(); + var args = Array.prototype.slice.apply(arguments); + Marionette.ItemView.prototype.close.apply(this, args); + }, - return result; - }, + // Add a single region, by name, to the layout + addRegion: function(name, definition){ + var regions = {}; + regions[name] = definition; + return this.addRegions(regions)[name]; + }, - // Handle closing regions, and then close the view itself. - close: function () { - if (this.isClosed) { - return; - } - this.regionManager.close(); - var args = Array.prototype.slice.apply(arguments); - Marionette.ItemView.prototype.close.apply(this, args); - }, + // Add multiple regions as a {name: definition, name2: def2} object literal + addRegions: function(regions){ + this.regions = _.extend(this.regions || {}, regions); + return this._buildRegions(regions); + }, - // Add a single region, by name, to the layout - addRegion: function (name, definition) { - var regions = {}; - regions[name] = definition; - return this.addRegions(regions)[name]; - }, + // Remove a single region from the Layout, by name + removeRegion: function(name){ + return this.regionManager.removeRegion(name); + }, - // Add multiple regions as a {name: definition, name2: def2} object literal - addRegions: function (regions) { - this.regions = _.extend(this.regions || {}, regions); - return this._buildRegions(regions); - }, + // internal method to build regions + _buildRegions: function(regions){ + var that = this; - // Remove a single region from the Layout, by name - removeRegion: function (name) { - return this.regionManager.removeRegion(name); - }, + var defaults = { + parentEl: function(){ return that.$el; } + }; - // internal method to build regions - _buildRegions: function (regions) { - var that = this; + return this.regionManager.addRegions(regions, defaults); + }, - var defaults = { - parentEl: function () { - return that.$el; - } - }; + // Internal method to initialize the regions that have been defined in a + // `regions` attribute on this layout. + _initializeRegions: function (options) { + var regions; + this._initRegionManager(); - return this.regionManager.addRegions(regions, defaults); - }, + if (_.isFunction(this.regions)) { + regions = this.regions(options); + } else { + regions = this.regions || {}; + } - // Internal method to initialize the regions that have been defined in a - // `regions` attribute on this layout. - _initializeRegions: function (options) { - var regions; - this._initRegionManager(); + this.addRegions(regions); + }, - if (_.isFunction(this.regions)) { - regions = this.regions(options); - } else { - regions = this.regions || {}; - } - - this.addRegions(regions); - }, - - // Internal method to re-initialize all of the regions by updating the `el` that - // they point to - _reInitializeRegions: function () { - this.regionManager.closeRegions(); - this.regionManager.each(function (region) { - region.reset(); - }); - }, - - // Internal method to initialize the region manager - // and all regions in it - _initRegionManager: function () { - this.regionManager = new Marionette.RegionManager(); - - this.listenTo(this.regionManager, "region:add", function (name, region) { - this[name] = region; - this.trigger("region:add", name, region); - }); - - this.listenTo(this.regionManager, "region:remove", function (name, region) { - delete this[name]; - this.trigger("region:remove", name, region); - }); - } + // Internal method to re-initialize all of the regions by updating the `el` that + // they point to + _reInitializeRegions: function(){ + this.regionManager.closeRegions(); + this.regionManager.each(function(region){ + region.reset(); }); + }, + + // Internal method to initialize the region manager + // and all regions in it + _initRegionManager: function(){ + this.regionManager = new Marionette.RegionManager(); + + this.listenTo(this.regionManager, "region:add", function(name, region){ + this[name] = region; + this.trigger("region:add", name, region); + }); + + this.listenTo(this.regionManager, "region:remove", function(name, region){ + delete this[name]; + this.trigger("region:remove", name, region); + }); + } +}); // AppRouter @@ -2034,7 +2011,7 @@ var Marionette = (function (global, Backbone, _) { // // Configure an AppRouter with `appRoutes`. // -// App routers can only take one `controller` object. +// App routers can only take one `controller` object. // It is recommended that you divide your controller // objects in to smaller pieces of related functionality // and have multiple routers / controllers, instead of @@ -2042,37 +2019,37 @@ var Marionette = (function (global, Backbone, _) { // // You can also add standard routes to an AppRouter. - Marionette.AppRouter = Backbone.Router.extend({ +Marionette.AppRouter = Backbone.Router.extend({ - constructor: function (options) { - Backbone.Router.prototype.constructor.apply(this, slice(arguments)); + constructor: function(options){ + Backbone.Router.prototype.constructor.apply(this, slice(arguments)); - this.options = options; + this.options = options; - if (this.appRoutes) { - var controller = Marionette.getOption(this, "controller"); - this.processAppRoutes(controller, this.appRoutes); - } - }, + if (this.appRoutes){ + var controller = Marionette.getOption(this, "controller"); + this.processAppRoutes(controller, this.appRoutes); + } + }, - // Internal method to process the `appRoutes` for the - // router, and turn them in to routes that trigger the - // specified method on the specified `controller`. - processAppRoutes: function (controller, appRoutes) { - var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes + // Internal method to process the `appRoutes` for the + // router, and turn them in to routes that trigger the + // specified method on the specified `controller`. + processAppRoutes: function(controller, appRoutes) { + var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes - _.each(routeNames, function (route) { - var methodName = appRoutes[route]; - var method = controller[methodName]; + _.each(routeNames, function(route) { + var methodName = appRoutes[route]; + var method = controller[methodName]; - if (!method) { - throw new Error("Method '" + methodName + "' was not found on the controller"); - } + if (!method) { + throw new Error("Method '" + methodName + "' was not found on the controller"); + } - this.route(route, methodName, _.bind(method, controller)); - }, this); - } - }); + this.route(route, methodName, _.bind(method, controller)); + }, this); + } +}); // Application @@ -2081,308 +2058,301 @@ var Marionette = (function (global, Backbone, _) { // Contain and manage the composite application as a whole. // Stores and starts up `Region` objects, includes an // event aggregator as `app.vent` - Marionette.Application = function (options) { - this._initRegionManager(); - this._initCallbacks = new Marionette.Callbacks(); - this.vent = new Backbone.Wreqr.EventAggregator(); - this.commands = new Backbone.Wreqr.Commands(); - this.reqres = new Backbone.Wreqr.RequestResponse(); - this.submodules = {}; +Marionette.Application = function(options){ + this._initRegionManager(); + this._initCallbacks = new Marionette.Callbacks(); + this.vent = new Backbone.Wreqr.EventAggregator(); + this.commands = new Backbone.Wreqr.Commands(); + this.reqres = new Backbone.Wreqr.RequestResponse(); + this.submodules = {}; - _.extend(this, options); + _.extend(this, options); - this.triggerMethod = Marionette.triggerMethod; - }; + this.triggerMethod = Marionette.triggerMethod; +}; - _.extend(Marionette.Application.prototype, Backbone.Events, { - // Command execution, facilitated by Backbone.Wreqr.Commands - execute: function () { - var args = Array.prototype.slice.apply(arguments); - this.commands.execute.apply(this.commands, args); - }, +_.extend(Marionette.Application.prototype, Backbone.Events, { + // Command execution, facilitated by Backbone.Wreqr.Commands + execute: function(){ + var args = Array.prototype.slice.apply(arguments); + this.commands.execute.apply(this.commands, args); + }, - // Request/response, facilitated by Backbone.Wreqr.RequestResponse - request: function () { - var args = Array.prototype.slice.apply(arguments); - return this.reqres.request.apply(this.reqres, args); - }, + // Request/response, facilitated by Backbone.Wreqr.RequestResponse + request: function(){ + var args = Array.prototype.slice.apply(arguments); + return this.reqres.request.apply(this.reqres, args); + }, - // Add an initializer that is either run at when the `start` - // method is called, or run immediately if added after `start` - // has already been called. - addInitializer: function (initializer) { - this._initCallbacks.add(initializer); - }, + // Add an initializer that is either run at when the `start` + // method is called, or run immediately if added after `start` + // has already been called. + addInitializer: function(initializer){ + this._initCallbacks.add(initializer); + }, - // kick off all of the application's processes. - // initializes all of the regions that have been added - // to the app, and runs all of the initializer functions - start: function (options) { - this.triggerMethod("initialize:before", options); - this._initCallbacks.run(options, this); - this.triggerMethod("initialize:after", options); + // kick off all of the application's processes. + // initializes all of the regions that have been added + // to the app, and runs all of the initializer functions + start: function(options){ + this.triggerMethod("initialize:before", options); + this._initCallbacks.run(options, this); + this.triggerMethod("initialize:after", options); - this.triggerMethod("start", options); - }, + this.triggerMethod("start", options); + }, - // Add regions to your app. - // Accepts a hash of named strings or Region objects - // addRegions({something: "#someRegion"}) - // addRegions({something: Region.extend({el: "#someRegion"}) }); - addRegions: function (regions) { - return this._regionManager.addRegions(regions); - }, + // Add regions to your app. + // Accepts a hash of named strings or Region objects + // addRegions({something: "#someRegion"}) + // addRegions({something: Region.extend({el: "#someRegion"}) }); + addRegions: function(regions){ + return this._regionManager.addRegions(regions); + }, - // Removes a region from your app. - // Accepts the regions name - // removeRegion('myRegion') - removeRegion: function (region) { - this._regionManager.removeRegion(region); - }, + // Removes a region from your app. + // Accepts the regions name + // removeRegion('myRegion') + removeRegion: function(region) { + this._regionManager.removeRegion(region); + }, - // Create a module, attached to the application - module: function (moduleNames, moduleDefinition) { - // slice the args, and add this application object as the - // first argument of the array - var args = slice(arguments); - args.unshift(this); + // Create a module, attached to the application + module: function(moduleNames, moduleDefinition){ + // slice the args, and add this application object as the + // first argument of the array + var args = slice(arguments); + args.unshift(this); - // see the Marionette.Module object for more information - return Marionette.Module.create.apply(Marionette.Module, args); - }, + // see the Marionette.Module object for more information + return Marionette.Module.create.apply(Marionette.Module, args); + }, - // Internal method to set up the region manager - _initRegionManager: function () { - this._regionManager = new Marionette.RegionManager(); + // Internal method to set up the region manager + _initRegionManager: function(){ + this._regionManager = new Marionette.RegionManager(); - this.listenTo(this._regionManager, "region:add", function (name, region) { - this[name] = region; - }); - - this.listenTo(this._regionManager, "region:remove", function (name, region) { - delete this[name]; - }); - } + this.listenTo(this._regionManager, "region:add", function(name, region){ + this[name] = region; }); + this.listenTo(this._regionManager, "region:remove", function(name, region){ + delete this[name]; + }); + } +}); + // Copy the `extend` function used by Backbone's classes - Marionette.Application.extend = Marionette.extend; +Marionette.Application.extend = Marionette.extend; // Module // ------ // A simple module system, used to create privacy and encapsulation in // Marionette applications - Marionette.Module = function (moduleName, app) { - this.moduleName = moduleName; +Marionette.Module = function(moduleName, app){ + this.moduleName = moduleName; - // store sub-modules - this.submodules = {}; + // store sub-modules + this.submodules = {}; - this._setupInitializersAndFinalizers(); + this._setupInitializersAndFinalizers(); - // store the configuration for this module - this.app = app; - this.startWithParent = true; + // store the configuration for this module + this.app = app; + this.startWithParent = true; - this.triggerMethod = Marionette.triggerMethod; - }; + this.triggerMethod = Marionette.triggerMethod; +}; // Extend the Module prototype with events / listenTo, so that the module // can be used as an event aggregator or pub/sub. - _.extend(Marionette.Module.prototype, Backbone.Events, { +_.extend(Marionette.Module.prototype, Backbone.Events, { - // Initializer for a specific module. Initializers are run when the - // module's `start` method is called. - addInitializer: function (callback) { - this._initializerCallbacks.add(callback); - }, + // Initializer for a specific module. Initializers are run when the + // module's `start` method is called. + addInitializer: function(callback){ + this._initializerCallbacks.add(callback); + }, - // Finalizers are run when a module is stopped. They are used to teardown - // and finalize any variables, references, events and other code that the - // module had set up. - addFinalizer: function (callback) { - this._finalizerCallbacks.add(callback); - }, + // Finalizers are run when a module is stopped. They are used to teardown + // and finalize any variables, references, events and other code that the + // module had set up. + addFinalizer: function(callback){ + this._finalizerCallbacks.add(callback); + }, - // Start the module, and run all of its initializers - start: function (options) { - // Prevent re-starting a module that is already started - if (this._isInitialized) { - return; - } + // Start the module, and run all of its initializers + start: function(options){ + // Prevent re-starting a module that is already started + if (this._isInitialized){ return; } - // start the sub-modules (depth-first hierarchy) - _.each(this.submodules, function (mod) { - // check to see if we should start the sub-module with this parent - if (mod.startWithParent) { - mod.start(options); - } - }); - - // run the callbacks to "start" the current module - this.triggerMethod("before:start", options); - - this._initializerCallbacks.run(options, this); - this._isInitialized = true; - - this.triggerMethod("start", options); - }, - - // Stop this module by running its finalizers and then stop all of - // the sub-modules for this module - stop: function () { - // if we are not initialized, don't bother finalizing - if (!this._isInitialized) { - return; - } - this._isInitialized = false; - - Marionette.triggerMethod.call(this, "before:stop"); - - // stop the sub-modules; depth-first, to make sure the - // sub-modules are stopped / finalized before parents - _.each(this.submodules, function (mod) { - mod.stop(); - }); - - // run the finalizers - this._finalizerCallbacks.run(undefined, this); - - // reset the initializers and finalizers - this._initializerCallbacks.reset(); - this._finalizerCallbacks.reset(); - - Marionette.triggerMethod.call(this, "stop"); - }, - - // Configure the module with a definition function and any custom args - // that are to be passed in to the definition function - addDefinition: function (moduleDefinition, customArgs) { - this._runModuleDefinition(moduleDefinition, customArgs); - }, - - // Internal method: run the module definition function with the correct - // arguments - _runModuleDefinition: function (definition, customArgs) { - if (!definition) { - return; - } - - // build the correct list of arguments for the module definition - var args = _.flatten([ - this, - this.app, - Backbone, - Marionette, - Marionette.$, _, - customArgs - ]); - - definition.apply(this, args); - }, - - // Internal method: set up new copies of initializers and finalizers. - // Calling this method will wipe out all existing initializers and - // finalizers. - _setupInitializersAndFinalizers: function () { - this._initializerCallbacks = new Marionette.Callbacks(); - this._finalizerCallbacks = new Marionette.Callbacks(); - } + // start the sub-modules (depth-first hierarchy) + _.each(this.submodules, function(mod){ + // check to see if we should start the sub-module with this parent + if (mod.startWithParent){ + mod.start(options); + } }); + // run the callbacks to "start" the current module + this.triggerMethod("before:start", options); + + this._initializerCallbacks.run(options, this); + this._isInitialized = true; + + this.triggerMethod("start", options); + }, + + // Stop this module by running its finalizers and then stop all of + // the sub-modules for this module + stop: function(){ + // if we are not initialized, don't bother finalizing + if (!this._isInitialized){ return; } + this._isInitialized = false; + + Marionette.triggerMethod.call(this, "before:stop"); + + // stop the sub-modules; depth-first, to make sure the + // sub-modules are stopped / finalized before parents + _.each(this.submodules, function(mod){ mod.stop(); }); + + // run the finalizers + this._finalizerCallbacks.run(undefined,this); + + // reset the initializers and finalizers + this._initializerCallbacks.reset(); + this._finalizerCallbacks.reset(); + + Marionette.triggerMethod.call(this, "stop"); + }, + + // Configure the module with a definition function and any custom args + // that are to be passed in to the definition function + addDefinition: function(moduleDefinition, customArgs){ + this._runModuleDefinition(moduleDefinition, customArgs); + }, + + // Internal method: run the module definition function with the correct + // arguments + _runModuleDefinition: function(definition, customArgs){ + if (!definition){ return; } + + // build the correct list of arguments for the module definition + var args = _.flatten([ + this, + this.app, + Backbone, + Marionette, + Marionette.$, _, + customArgs + ]); + + definition.apply(this, args); + }, + + // Internal method: set up new copies of initializers and finalizers. + // Calling this method will wipe out all existing initializers and + // finalizers. + _setupInitializersAndFinalizers: function(){ + this._initializerCallbacks = new Marionette.Callbacks(); + this._finalizerCallbacks = new Marionette.Callbacks(); + } +}); + // Type methods to create modules - _.extend(Marionette.Module, { +_.extend(Marionette.Module, { - // Create a module, hanging off the app parameter as the parent object. - create: function (app, moduleNames, moduleDefinition) { - var module = app; + // Create a module, hanging off the app parameter as the parent object. + create: function(app, moduleNames, moduleDefinition){ + var module = app; - // get the custom args passed in after the module definition and - // get rid of the module name and definition function - var customArgs = slice(arguments); - customArgs.splice(0, 3); + // get the custom args passed in after the module definition and + // get rid of the module name and definition function + var customArgs = slice(arguments); + customArgs.splice(0, 3); - // split the module names and get the length - moduleNames = moduleNames.split("."); - var length = moduleNames.length; + // split the module names and get the length + moduleNames = moduleNames.split("."); + var length = moduleNames.length; - // store the module definition for the last module in the chain - var moduleDefinitions = []; - moduleDefinitions[length - 1] = moduleDefinition; + // store the module definition for the last module in the chain + var moduleDefinitions = []; + moduleDefinitions[length-1] = moduleDefinition; - // Loop through all the parts of the module definition - _.each(moduleNames, function (moduleName, i) { - var parentModule = module; - module = this._getModule(parentModule, moduleName, app); - this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs); - }, this); + // Loop through all the parts of the module definition + _.each(moduleNames, function(moduleName, i){ + var parentModule = module; + module = this._getModule(parentModule, moduleName, app); + this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs); + }, this); - // Return the last module in the definition chain - return module; - }, + // Return the last module in the definition chain + return module; + }, - _getModule: function (parentModule, moduleName, app, def, args) { - // Get an existing module of this name if we have one - var module = parentModule[moduleName]; + _getModule: function(parentModule, moduleName, app, def, args){ + // Get an existing module of this name if we have one + var module = parentModule[moduleName]; - if (!module) { - // Create a new module if we don't have one - module = new Marionette.Module(moduleName, app); - parentModule[moduleName] = module; - // store the module on the parent - parentModule.submodules[moduleName] = module; - } + if (!module){ + // Create a new module if we don't have one + module = new Marionette.Module(moduleName, app); + parentModule[moduleName] = module; + // store the module on the parent + parentModule.submodules[moduleName] = module; + } - return module; - }, + return module; + }, - _addModuleDefinition: function (parentModule, module, def, args) { - var fn; - var startWithParent; + _addModuleDefinition: function(parentModule, module, def, args){ + var fn; + var startWithParent; - if (_.isFunction(def)) { - // if a function is supplied for the module definition - fn = def; - startWithParent = true; + if (_.isFunction(def)){ + // if a function is supplied for the module definition + fn = def; + startWithParent = true; - } else if (_.isObject(def)) { - // if an object is supplied - fn = def.define; - startWithParent = def.startWithParent; + } else if (_.isObject(def)){ + // if an object is supplied + fn = def.define; + startWithParent = def.startWithParent; + + } else { + // if nothing is supplied + startWithParent = true; + } - } else { - // if nothing is supplied - startWithParent = true; - } + // add module definition if needed + if (fn){ + module.addDefinition(fn, args); + } - // add module definition if needed - if (fn) { - module.addDefinition(fn, args); - } + // `and` the two together, ensuring a single `false` will prevent it + // from starting with the parent + module.startWithParent = module.startWithParent && startWithParent; - // `and` the two together, ensuring a single `false` will prevent it - // from starting with the parent - module.startWithParent = module.startWithParent && startWithParent; + // setup auto-start if needed + if (module.startWithParent && !module.startWithParentIsConfigured){ - // setup auto-start if needed - if (module.startWithParent && !module.startWithParentIsConfigured) { - - // only configure this once - module.startWithParentIsConfigured = true; - - // add the module initializer config - parentModule.addInitializer(function (options) { - if (module.startWithParent) { - module.start(options); - } - }); - - } + // only configure this once + module.startWithParentIsConfigured = true; + // add the module initializer config + parentModule.addInitializer(function(options){ + if (module.startWithParent){ + module.start(options); } - }); + }); + + } + + } +}); - return Marionette; + + return Marionette; })(this, Backbone, _); \ No newline at end of file diff --git a/templates/views/MixListView.html b/templates/views/MixListView.html index dd0569e..fa85261 100644 --- a/templates/views/MixListView.html +++ b/templates/views/MixListView.html @@ -1,9 +1,9 @@ -