From e78f049bbb0b0b0763a442891e10c89305039a97 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Fri, 14 Jun 2013 14:36:17 +0100 Subject: [PATCH] Major refactor of inter-view messaging. Added central eventAggregator and audioController. Removed a lot of audio handling from the view and a lot of view logic from player. Still some work to go --- dss/settings.py | 2 +- spa/models/activity.py | 10 +- static/css/deepsouthsounds.css | 54 + static/css/testing.css | 1043 ++ static/img/bg.gif | Bin 0 -> 11055 bytes static/img/player-sprite.png | Bin 0 -> 8325 bytes static/js/app/appv2.coffee | 7 +- static/js/app/appv2.js | 3 +- static/js/app/dss.bootstrapper.js | 13 +- static/js/app/lib/audioController.coffee | 35 + static/js/app/lib/audioController.js | 62 + static/js/app/lib/eventAggregator.coffee | 7 +- static/js/app/lib/eventAggregator.js | 21 +- static/js/app/models/user/userCollection.js | 9 +- static/js/app/views/mix/mixItemView.coffee | 98 +- static/js/app/views/mix/mixItemView.js | 117 +- static/js/app/views/mix/mixListView.coffee | 17 +- static/js/app/views/mix/mixListView.js | 13 +- .../js/app/views/sidebar/sidebarView.coffee | 33 +- static/js/app/views/sidebar/sidebarView.js | 31 +- static/js/app/views/user/userListView.coffee | 4 +- static/js/app/views/user/userListView.js | 16 +- .../app/views/widgets/nowPlayingView.coffee | 48 + static/js/app/views/widgets/nowPlayingView.js | 68 + static/js/com.podnoms.player.js | 26 - static/js/com.podnoms.utils.js | 2 +- .../js/libs/backbone/backbone.babysitter.js | 177 + .../js/libs/backbone/backbone.marionette.js | 479 +- static/js/libs/backbone/backbone.wreqr.js | 276 + static/js/libs/jquery.dataTables.js | 12098 ++++++++++++++++ templates/javascript/settings.js | 4 +- templates/views/NowPlayingView.html | 30 + templates/views/SidebarView.html | 4 +- templates/views/_MixItemInsert.html | 4 +- 34 files changed, 14151 insertions(+), 660 deletions(-) create mode 100644 static/css/testing.css create mode 100644 static/img/bg.gif create mode 100644 static/img/player-sprite.png create mode 100644 static/js/app/lib/audioController.coffee create mode 100644 static/js/app/lib/audioController.js create mode 100644 static/js/app/views/widgets/nowPlayingView.coffee create mode 100644 static/js/app/views/widgets/nowPlayingView.js create mode 100644 static/js/libs/backbone/backbone.babysitter.js create mode 100644 static/js/libs/backbone/backbone.wreqr.js create mode 100644 static/js/libs/jquery.dataTables.js create mode 100644 templates/views/NowPlayingView.html diff --git a/dss/settings.py b/dss/settings.py index fd3d605..c2037bd 100644 --- a/dss/settings.py +++ b/dss/settings.py @@ -141,7 +141,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'spa.middleware.uploadify.SWFUploadMiddleware', #'spa.middleware.sqlprinter.SqlPrintingMiddleware' if DEBUG else None, - 'debug_toolbar.middleware.DebugToolbarMiddleware', + #'debug_toolbar.middleware.DebugToolbarMiddleware', ) WSGI_APPLICATION = 'dss.wsgi.application' diff --git a/spa/models/activity.py b/spa/models/activity.py index 92ff297..0cdcbfc 100644 --- a/spa/models/activity.py +++ b/spa/models/activity.py @@ -30,7 +30,7 @@ class ActivityMix(Activity): return self.mix.title def get_object_url(self): - return self.mix.get_full_url() + return self.mix.get_absolute_url() def get_object_singular(self): return "mix" @@ -42,7 +42,7 @@ class ActivityFavourite(Activity): return self.mix.title def get_object_url(self): - return self.mix.get_full_url() + return self.mix.get_absolute_url() def get_object_singular(self): return "mix" @@ -57,7 +57,7 @@ class ActivityPlay(Activity): return self.mix.title def get_object_url(self): - return self.mix.get_full_url() + return self.mix.get_absolute_url() def get_object_singular(self): return "mix" @@ -73,7 +73,7 @@ class ActivityLike(Activity): return self.mix.title def get_object_url(self): - return self.mix.get_full_url() + return self.mix.get_absolute_url() def get_object_singular(self): return "mix" @@ -89,7 +89,7 @@ class ActivityDownload(Activity): return self.mix.title def get_object_url(self): - return self.mix.get_full_url() + return self.mix.get_absolute_url() def get_object_singular(self): return "mix" diff --git a/static/css/deepsouthsounds.css b/static/css/deepsouthsounds.css index 5879cd4..606c859 100644 --- a/static/css/deepsouthsounds.css +++ b/static/css/deepsouthsounds.css @@ -16,6 +16,7 @@ body { padding-top: 45px; padding-bottom: 40px; padding-right: 32px; + background: url('../img/bg.gif'); } /* IE/Chrome image fix */ @@ -426,4 +427,57 @@ div.event-content td { to { background-position: -50px 0; } +} + +.now-playing-image { + width: 32px; + height: 32px; +} + +.accessible-description { + display: none !important; +} + +.player-button { + width: 32px; + height: 32px; + background: url(../img/player-sprite.png); + cursor: pointer; + color: #f77f00; + text-decoration: none; + display: inline-block; +} + +.now-playing-play { + background-position: -90px 0px; +} +.now-playing-pause{ + background-position: -210px 0px; +} +.now-playing-bio p { + display: inline-block; + margin-left: 5px; +} + +#aaaa{ + border: 1px solid #802c59; + + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.now-playing-image-container { + display: inline-block; +} + +#sidebar-top-content { + margin-bottom: 12px; + padding: 8px; +} + +.div-small-heading{ + width: 98%; + text-align: center; + margin-bottom: 8px; } \ No newline at end of file diff --git a/static/css/testing.css b/static/css/testing.css new file mode 100644 index 0000000..41b0087 --- /dev/null +++ b/static/css/testing.css @@ -0,0 +1,1043 @@ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline +} + +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { + display: block +} + +body { + line-height: 1 +} + +ol, ul { + list-style: none +} + +blockquote, q { + quotes: none +} + +blockquote:before, blockquote:after, q:before, q:after { + content: ''; + content: none +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +body { + margin: 0; + padding: 0 +} + +.container { + max-width: 920px; + padding: 0 20px; + margin: 0 auto +} + +.clear { + clear: both +} + +@font-face { + font-family: 'Copse'; + font-style: normal; + font-weight: 400; + src: local('Copse'), url(http://themes.googleusercontent.com/static/fonts/copse/v3/GTNBLj58pwv6qyDO1RGnXaCWcynf_cDxXwCLxiixG1c.woff) format('woff') +} + +body { + font-family: 'Copse'; + font-size: 16px; + line-height: 22px; + color: #373737; + -moz-font-smoothing: antialiased; + font-smoothing: antialiased; + -webkit-font-smoothing: antialiased; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004) +} + +hgroup { + margin-bottom: 15px +} + +hgroup h2 { + margin-bottom: 10px +} + +h2 { + font-size: 36px; + font-style: italic +} + +h3 { + font-size: 30px; + font-style: italic +} + +a { + color: inherit; + text-decoration: none +} + +.italic { + font-style: italic +} + +.bold { + font-weight: bold +} + +.grey { + color: #9b9b9b +} + +.underline a, a.underline { + border-bottom: 2px solid; + padding-bottom: 3px; + position: relative +} + +.underline a span, a.underline span { + position: absolute; + right: -23px; + font-size: 16px; + bottom: -12px; + padding: 0px 3px; + background-color: #fff +} + +.underline-yellow a, a.underline-yellow { + border-color: #f1e52f +} + +.underline-green a, a.underline-green { + border-color: #63a544 +} + +.underline-blue a, a.underline-blue { + border-color: #26acec +} + +.underline-purple a, a.underline-purple { + border-color: #3b5998 +} + +.underline-orange a, a.underline-orange { + border-color: #f60 +} + +img { + max-width: 100% +} + +hr { + border: none; + color: #9b9b9b; + background-color: #9b9b9b; + height: 2px +} + +input[type=text], input[type=email], input[type=url] { + background-color: #dedede; + width: 205px; + padding: 8px 5px; + border-top: none; + border-left: none; + border-right: none; + border-bottom: 2px solid #63a544; + font-family: 'Copse'; + font-size: 12px +} + +input { + outline: none; + margin-bottom: 20px; + display: block; + font-family: 'Copse' +} + +input[type=text]:focus, input[type=email]:focus { + outline: none; + border-bottom: 2px solid #f1e52f +} + +input[type=submit] { + background-color: #63a544; + color: white; + font-size: 12px; + line-height: 20px; + padding: 5px 17px; + border: none; + -webkit-appearance: none; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box +} + +input[type=submit]:hover { + background-color: #cbd13e +} + +.button { + display: inline-block; + color: white; + font-size: 12px; + line-height: 20px; + padding: 5px 17px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box +} + +.button:hover { + background-color: #cbd13e +} + +.green-button { + background-color: #63a544 +} + +#header { + background: url('/wp-content/themes/fivenew/img/header-background.png'); + width: 100% +} + +#header #topNav { + background-color: #171717; + -webkit-box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.55); + -moz-box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.55); + box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.55) +} + +.modalDialog { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, 0.8); + z-index: 99999; + opacity: 0; + -webkit-transition: opacity 400ms ease-in; + -moz-transition: opacity 400ms ease-in; + transition: opacity 400ms ease-in; + pointer-events: none +} + +.modalDialog:target { + opacity: 1; + pointer-events: auto +} + +.modalDialog>div { + max-width: 570px; + position: relative; + margin: 10% auto +} + +.close { + background: #606061; + color: #FFF; + line-height: 25px; + position: absolute; + right: -12px; + text-align: center; + top: -10px; + width: 24px; + text-decoration: none; + font-weight: bold; + -webkit-border-radius: 12px; + -moz-border-radius: 12px; + border-radius: 12px; + -moz-box-shadow: 1px 1px 3px #000; + -webkit-box-shadow: 1px 1px 3px #000; + box-shadow: 1px 1px 3px #000 +} + +.close:hover { + background: #63a544 +} + +#openModal #newsletter form input[type=email] { + margin-right: 0; + text-align: center +} + +footer { + background-color: #171717 +} + +footer .container { + padding-top: 35px; + padding-bottom: 35px; + color: white +} + +footer .container h3 { + margin-bottom: 10px +} + +footer .container .twitter .inner .twitterInfo { + padding: 10px 0 15px; + border-bottom: 1px solid #494949; + margin-bottom: 15px +} + +footer .container .twitter .inner .twitterHandle { + color: #63a544; + display: inline-block; + margin-right: 15px +} + +footer .container .twitter .inner ul li { + margin-bottom: 20px +} + +footer .container #cats { + margin-top: 10px; + border-top: 1px solid #494949; + padding: 30px 0 +} + +footer .container #cats nav ul li { + margin-bottom: 15px +} + +footer .container #cats nav ul li a { + border-bottom: 1px solid #63a544 +} + +footer .container .copy { + border-top: 1px solid #494949; + padding: 30px 0; + font-size: 12px +} + +footer .container .copy p span { + padding: 0 5px +} + +footer .container .copy .twitter, footer .container .copy .rss, footer .container .copy .facebook { + background: url('/wp-content/themes/fivenew/img/sprite.png') no-repeat; + display: inline-block +} + +footer .container .copy .twitter { + background-position: 0px 0px; + height: 22px; + width: 23px; + margin-right: 11px +} + +footer .container .copy .rss { + background-position: 0px -64px; + width: 26px; + height: 22px +} + +footer .container .copy .facebook { + background-position: 0px -32px; + width: 17px; + height: 22px; + margin-right: 11px +} + +#about .container { + border-top: 2px solid #9b9b9b; + padding-top: 25px; + padding-bottom: 25px +} + +#about .container h3 { + margin-bottom: 15px +} + +#about .container p { + margin-bottom: 10px +} + +#archive { + padding-top: 30px +} + +.mainSection { + width: 66%; + margin-right: 10%; + float: left +} + +.mainSection h1 { + font-family: 'Copse'; + font-size: 36px; + line-height: 1em; + margin-bottom: 18px +} + +.mainSection hr { + border: none; + color: #9b9b9b; + margin: 30px 0; + background-color: #9b9b9b; + height: 1px +} + +.mainSection h1+p { + font-size: 19px; + line-height: 1.4em +} + +.mainSection p, .mainSection ul, .mainSection blockquote { + margin-bottom: 20px +} + +.mainSection blockquote { + font-family: 'Copse'; + font-size: 20px; + line-height: 28px; + color: #63a544; + background: url('/wp-content/themes/fivenew/img/blockquote.png') no-repeat top left; + padding-left: 65px; + min-height: 100px +} + +.mainSection blockquote p { + margin: 0 +} + +.mainSection h2, .mainSection h3, .mainSection h4, .mainSection h5, .mainSection h6 { + font-size: 26px; + line-height: 32px; + margin-bottom: 18px +} + +.mainSection img { + margin: 10px 0 +} + +.mainSection pre { + font: 13px "Courier 10 Pitch", Courier, monospace; + line-height: 1.5; + margin-bottom: 1.625em; + overflow: auto; + padding: 1.625em; + background: #DDDDDB; + white-space: pre-wrap; + white-space: -moz-pre-wrap; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + word-wrap: break-word +} + +.mainSection code, .mainSection kbd, .mainSection samp, .mainSection var { + font: 13px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; + color: #666; + background: #DDDDDB; + display: block; + padding: 5px +} + +.mainSection code { + display: inline +} + +.excerptHide { + height: 1000px; + overflow: hidden +} + +.excerptMore { + border-top: 1px dashed #171717; + padding-top: 20px; + margin-bottom: 50px +} + +.excerptMore span { + font: 13px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; + display: block; + margin-bottom: 20px +} + +.sidebar { + width: 23%; + float: left +} + +.sidebar article { + margin-bottom: 50px +} + +.sidebar h4 { + font-size: 16px; + line-height: 23px +} + +.sidebar p { + font: 13px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; + display: block; + margin-bottom: 20px +} + +.sidebar img { + margin: 10px 0 +} + +.sidebar .author { + background-color: #e7e7e7; + padding: 0px 10px 10px 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + margin-bottom: 50px +} + +.sidebar .author .authorName { + max-width: 150px; + margin-left: 10px; + margin-top: 20px; + font-size: 16px; + color: #373737 +} + +.sidebar .author .authorName.noPic { + margin-left: 0 +} + +.sidebar .author .authorName.noPic span { + display: block; + padding-top: 10px; + margin-bottom: 20px +} + +.sidebar .author a { + font-size: 16px; + color: #373737; + word-break: break-all +} + +.catTitle { + border-bottom: 1px solid #9b9b9b +} + +body.archive #archive .mainSection article h4 { + font-size: 16px; + line-height: 22px; + color: #373737; + font-weight: bold +} + +.authorTitle { + font-size: 26px; + line-height: 30px +} + +#todays-five { + padding: 25px 0 +} + +#todays-five article { + padding: 25px 0; + text-align: center +} + +#todays-five article h2, #todays-five article p { + margin-left: 10%; + margin-right: 10% +} + +#todays-five article h2 { + margin-bottom: 22px +} + +#todays-five article h2 a { + font-size: 36px; + color: #373737 +} + +#todays-five article p { + margin-bottom: 12px +} + +#newsletter-section { + background-color: #e7e7e7; + padding: 35px 50px; + max-width: 520px; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + margin: 0 auto 50px auto; + text-align: center +} + +#newsletter-section h2 { + line-height: 1em; + margin-bottom: 8px; + font-style: normal +} + +#newsletter-section p { + margin-bottom: 25px +} + +#newsletter-section form { + margin: 0 auto +} + +#newsletter-section form input { + display: inline +} + +#newsletter-section form input[type=email] { + margin-right: 20px; + background-color: white +} + +#newsletter article { + background-color: #e7e7e7; + padding: 35px 50px; + max-width: 520px; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + margin: 0 auto 50px auto; + text-align: center +} + +#newsletter article h2 { + line-height: 1em; + margin-bottom: 8px; + font-style: normal +} + +#newsletter article p { + margin-bottom: 25px +} + +#newsletter article form { + margin: 0 auto +} + +#newsletter article form input { + display: inline +} + +#newsletter article form input[type=email] { + margin-right: 20px; + background-color: white +} + +html.oldie { + font-family: Arial +} + +html.oldie .left { + float: left +} + +html.oldie .right { + float: right +} + +html.oldie .third { + width: 33.33333333%; + display: block; + float: left +} + +html.oldie .half { + width: 50%; + float: left; + display: block +} + +html.oldie .modalDialog { + display: none +} + +html.oldie #topNav nav { + max-width: 960px; + margin: 0 auto +} + +html.oldie #topNav ul { + overflow: hidden +} + +html.oldie #topNav ul.left { + float: left +} + +html.oldie #topNav ul.right { + float: right +} + +html.oldie #topNav ul li { + float: left; + display: block; + padding: 25px 0; + margin-right: 40px +} + +html.oldie #topNav ul li a { + color: #fff; + font-family: Arial; + font-size: 16px; + line-height: 22px +} + +html.oldie .five-logo { + padding: 100px 0; + display: block; + background: url('/wp-content/themes/fivenew/img/logo-five.png') center center no-repeat +} + +html.oldie #todays-five h2 { + font-family: 'Georgia', serif; + font-size: 36px; + line-height: 22px; + font-weight: bold +} + +html.oldie #todays-five .third { + width: 33.33333333%; + float: left; + display: block +} + +html.oldie #todays-five .half { + width: 50%; + float: left; + display: block +} + +html.oldie #newsletter-section { + background-color: #e7e7e7; + padding: 35px 50px; + max-width: 520px; + display: block; + margin: 0 auto 50px auto; + text-align: center +} + +html.oldie #newsletter-section h2 { + line-height: 1em; + margin-bottom: 8px; + font-style: normal +} + +html.oldie #newsletter-section p { + margin-bottom: 25px +} + +html.oldie #newsletter-section form { + margin: 0 auto +} + +html.oldie #newsletter-section form input { + display: inline; + color: black +} + +html.oldie #newsletter-section form input[type=email] { + margin-right: 20px; + background-color: white +} + +html.oldie #gform_1 .gform_description { + max-width: 280px; + display: block; + margin-bottom: 30px +} + +html.oldie #cats { + margin-top: 10px; + border-top: 1px solid #494949; + padding: 30px 0 +} + +html.oldie #cats h3 { + margin-right: 30px +} + +html.oldie #cats nav ul { + overflow: hidden +} + +html.oldie #cats nav ul li { + margin-bottom: 15px; + display: block; + float: left; + width: 25% +} + +html.oldie #cats nav ul li a { + border-bottom: 1px solid #63a544 +} + +html.oldie .social { + text-align: right +} + +html.oldie .twitter .inner { + border-left: 1px solid #494949; + padding: 0 70px 20px 70px +} + +html.oldie .author { + overflow: hidden +} + +html.oldie #archive .mainSection li { + margin-bottom: 20px; + float: left; + width: 50% +} + +html.oldie #archive .mainSection li article { + vertical-align: top; + display: block; + padding-right: 30px; + word-break: break-word; + height: 365px +} + +html.oldie #archive .mainSection li article p { + max-height: 286px; + overflow: hidden +} + +html[data-useragent*='MSIE 10.0'] .modalDialog { + display: none +} + +@media (max-width:480px) { + #topNav nav ul li { + padding: 5px + } + + #topNav nav ul li a { + font-size: 13px + } +} + +@media (min-width:481px) and (max-width:764px) { + #topNav nav ul li { + padding: 5px 10px + } +} + +@media (max-width:764px) { + .container { + text-align: center + } + + form input { + margin-left: auto; + margin-right: auto + } + + #topNav { + padding: 20px 0 + } + + #topNav nav ul { + text-align: center + } + + #topNav nav ul.social { + display: none + } + + #topNav nav ul li { + display: inline-block; + float: none; + margin: 0; + width: auto; + text-align: center; + font-size: 14px + } + + #topNav nav ul li a { + color: #e7e7e7 + } + + #archive .mainSection { + width: 100%; + margin-right: auto; + margin-left: auto; + float: none + } + + #archive .mainSection ul li { + margin-bottom: 30px; + float: none; + width: 100% + } + + #archive .sidebar { + display: none + } + + .container { + padding: 0 10px + } + + .container .five-logo { + background: url('/wp-content/themes/fivenew/img/logo-five.png') no-repeat right; + padding: 50px 0 + } + + .submit { + margin-bottom: 30px + } + + .submit form input { + margin: 0 auto 20px auto + } + + .submit .gform_heading { + margin-bottom: 10px + } + + .close { + right: 5px; + top: -4px + } + + #cats h3 { + margin-bottom: 20px + } + + #cats nav ul li { + width: 100%; + list-style: none + } +} + +@media (min-width:764px) { + .container .five-logo { + padding: 40px 0; + background: url('/wp-content/themes/fivenew/img/logo-five.png') no-repeat center + } + + body.home .container .five-logo { + padding: 100px 0; + background: url('/wp-content/themes/fivenew/img/logo-five.png') no-repeat center + } + + #archive .mainSection li { + margin-bottom: 20px; + float: left; + width: 50% + } + + #archive .mainSection li article { + vertical-align: top; + display: block; + padding-right: 30px; + word-break: break-word; + height: 365px + } + + #archive .mainSection li article p { + max-height: 286px; + overflow: hidden + } + + #topNav nav { + max-width: 940px; + margin: 0 auto + } + + #topNav nav ul { + padding-left: 10px + } + + #topNav nav ul.social { + padding: 0 + } + + #topNav nav ul.social li { + margin-right: 35px + } + + #topNav nav ul.social li a { + font-size: 12px + } + + #topNav nav ul li { + float: left; + margin-right: 40px; + padding: 25px 0 + } + + #topNav nav ul li a { + color: #e7e7e7 + } + + .left { + float: left + } + + .right { + float: right + } + + .half { + width: 50% + } + + .third { + width: 33.33333333% + } + + .twitter .inner { + border-left: 1px solid #494949; + padding: 0 70px 20px 70px + } + + #gform_1 .gform_description { + max-width: 280px; + display: block; + margin-bottom: 30px + } + + #cats h3 { + margin-right: 30px + } + + #cats form input[type=text] { + margin-right: 10px + } + + #cats nav ul li { + float: left; + width: 25%; + list-style: disc; + list-style-position: inside + } + + .social { + text-align: right + } +} \ No newline at end of file diff --git a/static/img/bg.gif b/static/img/bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..046575bb9cd81c513989e2e825c5ef9371ca8277 GIT binary patch literal 11055 zcmV+~E6~(ONk%w1VI2Y50FeLy@%Y^L{o(ol-J@rU-$;owy8Pm2#~R3k#71fA2W8EWLO#}ntB1!Qmohu7 zZLDO^0vH~KO3kyP6jvEFRqml9C-F^{UAA^OJLb>S$!cFeaMKjCZJmU4+qhddE0alz zKnaT4l#b!sZpai}BZy0wQE4hq*+_P>*DM2fM~$VEDwG_{Wh38d2MwLla>B4Z_8Z2H z+*>b0CiWB|^+<*{UtT~|MRw3EyVK$c|D~ga2sd z3uVdVzcV6lDv~SpkWqI(--XYQ_}mZG+LWaWxKY^N^v++ns$kHmWYhis^WR2kuAD&_ zG|-K)UK0E@wA*6}8Fx-EI_P6eafVo?USQX`6&O&TOmfFw?}%sJCyB{+p-+}MG8tAQ zeh`%wC_MGtD%ZfMj2R)e5=d(M_!XBO6_u49YCjRd(l*=#^iMwa(6NPQZN*b#7|hw% z+*5D8;S);M1b5t2-IY`id)pOwl^8lwah+gRX&D)Epdhj2Wo|Jw4??O)A=6q3CIeSj z0IInoo}F2#)kGsI_+4XH<_Cxm6EViwqw0+o*Ot$0v}SvRF@R=z!vVS$4ECv3(*e#c ziAI>8ZWfV#YJF)WJR0t#89CWORn}D`(g3NRwz_qU2k+>z8#t~1;I!IokRHMgFmkr! z$7uzzm<553LF=BX$OI$hi5EJEAzJ|DM;mIwZHq-|M-7t=SWN*ZDqAY10gejl(9mmy z0iuWPp^Cr`k+ZpyFL}PDn-YcJQ{e&Rhtc zoLZ{DLf9t7sJ3{Gd=+lkA(DKEn-Z?WStuDv{{h%YsS=7J$QeNbeMiOBF4k3AvsWJ_f71O?nut0P6%#y^eGWD`kURz8LGonqp;ZnT+MPlZmbKZH2W|1!EO;Y$- z)yNzHM}hH0yy+AmCoT*Dl?(DL;SXwqhAxD(i5nwnWD>^TwwJAF zFr_h&cj)8_3mWh`5Gvcd&a}DPl&%q5qZN`EH;0b@kcL)!C|W~i7oxvl#&jxkM#FTL z0huT;0WF$R`ATrJEG&;#zN292OcS0?sUT79@gQdQbFHfEjB>JCi#c3{L5h)#LP<)F z7H#(%g5?2s>=gd<7M@$ARtT<-u)UdY>=mwDXQ_a4V$fr0YFNvvaonglH zH;P!$No3O?DH#a4P5$VCU`qv(^cpolxz1@j1Zd&b=b?(mVVJc8Cw;J_Q=J*jg-=LeX-tIU#8gc%*O9Y!OkkNL?7(rU-5_je9GZveZaX61G!N zIvUF8Fa^_Ba4K(D;z_53%Bs-%)s#>FOlw*@@xYL^DKPh(9w&7-T6to%oEUNBMiu4C zm4=aH#zM=T@S$B4fzwJwm4XHbaa*eC=9PyTC5l88mf3WSKLQ-Dcl+p1=n{q)rVUm{ zn-MP`X{VYI!x;{D>Ljc!(!M-87)9kMJ!sLhX_FJ{gGE~$qHH0F)u`l?BKnRyYS$yw zGVrE+W?5}1XhhkW6{XXwlczK@eC~%&c<Qb#b>^I>HWMS!FY%nF*0Rd@V#4r7r1?1)f8bNQ1Lg z=J!KE-u1gx^@dH6u2wm6a|6-JD0jBixAWcI;ivyM$j^gL)+nZ0GC8Ir?aFnkq&j3R++32#fZ|cTn zXHtWm82m?U(CH4A(9;olka>Ze)OX)hD#eaa1vselE}5(;Ay}s8S^uY zN{DQGDHCW%3T|C{@1ghqswWq+&QylA8Z@P27IX))&hH`=F<&k7l*P5X5p>b0%e%pC z&*`~XzFxn3ncNBq>#=5wF6gpS4z!P_k@2=3dHhL0#NG5W$NM#;t!Be70hn!j^LV@3 zo^SBoE|Z1wGtSmj>@%Sp>yVVMmYXP-#?l6nE~QJ@dOHto+VHlNU*X^-7tBOT(@k{Y zEDd3|QnU@Me;r5TsJG6(5Y3Hjhe8Q2Mr9&|}3b|0$7 zQOlQ0cA;`v6eTs~EB~ZqlLlwa7kmDdC3-*!Uc+M9v{hnLGWjH3-4SxCR0#=(Be60d z*hFV1g@S|f5q*>(;D$BsuyKurA@HOneDqb4lR!n(D z0M{R6@-)K#V16TjPq!9{S2GPnvM2miQuxw@jRYlCqfcGKT2SP2`6Di7XkxD;D<{@T z_OwvKbxbr&7YeZw{|IVBKqjjQ zNkRU3cwSNAnCp{97~7K1`K6%e6&S9EwI>4N`dJG;mvQ^rZ4w`Hj`ELRuyPY z7(xtrPUUxDjHh?-r6zioSsbZr95#7j zW)d0|9egNg9Tj{f78b;YX_UxAD?mlJ*)7mfVfNS>p5Qq6qGZJo^cbbd{BA?Sak@;d>HEqcwN(}0sANeRtL}e+NZjJJgk;qY@sgu{~A|hcl2g3$^ z$ZL26HMqu#`m_wR_g9poj8=(+tW;WS#ZowyIxp2C1U96k6EBR$T~Fpr3Dkm7npai! zWid1%2)Gorbdk)Ir%m-DMd6c48KmZUFMMZV{fRwpb$tJpPE<8ELvESgi3za#BOy6RZfX~5=9`40VVP&Q2S#pj{2*|c%D^Oil|s} zKw^fD$S+A}D?7quO@mgAmvp^3Kn6rv50fcAhFr~|3g_i<%mzkJsYT0Lf^*1bI@e}1 zX?Ge_8P*{wnSoaE^PFUfDd=;iJ=qhN=NZ6~Ovh44+*C5e@uW1?KaGm7h&gOVL}&RS zWW~9FS)-2X_j3eym?F?GhWK6VRU&MN9*h=ga)g~UxN>$TQhEgtl~jN;Ghf0pUBgO> zK02W8L4<5KrMyL#Y6+hZ6((y$hr`CFvIu_-=~a2U71JqHyVruS0$(xzW>-FWp(x8& zX&4&m2RpBn5}pP#GliIL_L<0-sj`9?Y-z4qD|wz~h%TqJW%e;^ctkP6nm^_=rt^H+ znk1aI8r(-#{NZH0b24CtB}jpZM@4*Y8@9R@w-yU0F=MVjIallFZl4LZ0XdAkBvQ4i zaBG)&4|k`ENDXe+c}G1J;XWebsCgTWM6*sWE z6p)H14FW2XC}nA6%7b&)rF=C{CnYnewY!PRK??OS?|UgZYE9+qzk0fEcNcdDVr{Vb zW~3S&DVtGz@}U6?l=r88xsiK=OMSr0i+vWC2~(`QczjQ}feVt7hbMA}M>|ECggDYP zM_Hwe6nPlD6QmNrJh*PWfG7e&Y+@sx#dE{{2CIK3cK?yHW_N$dl_-BglL}g5_JzBN zFc9KtuV9+2G&hnJbT=TihU^Gk1hj=42^SzFTyrc=#^gs>nQOmmQbR;c$9hjARYR7_ zIf7wzS9vg$`-?2tL*qBCrD!-sHX5>Xpqqd{e>=OAppef0bHGpRqTnbJE#oD^iF4_t zl#v=J#wN3om}b{Ef42#N1N623=sGBQ5d%n^=$US>c3M_D!cw9=)q7T0b$esnxQ`adc{mD6;}<5;`WU(x@4h zPT)Mk8Z(sk$8qtNJM5`eOt&TI=)^OoV@G*+E!kfDXHGY#tGy$@V&|8{ipS;Vz}drr z>65oye0p(&iMsMDVk?g4*UIVG%AdG($r+}X2ea1yNnX1HtKPgV7KngB@@!43)QFp( zz`M-aBfi;6g%>gKA}k(8-th$@#`sb{ZqI5y;2YNDrvIdW!cbA)@OQ}g7MY(hmD z_^_d;vgR|&pQe*kT`;@&&y-|bp|X6>$&*~>SBx`G)F3LQ zbreX(`USWSZBK9M!71HJc$tvmbER)LL%%m$rrfMnZA>KFkKuWWDKvjcRkxC<(vb!g z2B@6ms-RP)lr~1&?G>5E>dcKrQkB>ks+w~c#|zC`uVgvQM!D3UiPq?f*+R%j!4zU* zxvY!Aj8vQ5PiP(+oO8+aLHsMPp4pDI8gX&|%+^vDzw2bEjyr-`r&?O&Sq_A1liiLk z9hjg5(|a6WKfPbkB`C@}qtbfDvsZ5ovnf#lMs+M-+_H&8F@B~9D8+k4_KMJ&x|%uZ zc4Ss#%4cFQcCI6Ipvf{rqu4}|lGfoFVeT1i`xMV`$vkNG4JyRp1ofJyQ&&zLZgMEr zq??#es)SBBtTTVJ_L2DyJIo_8-Qu3o8^oEWgS=n)>_kif*mv+RDY=YZS_fxef=iiW8CY{_~^ zS$aG(qs@Hxrn#Y;-z@81?RKkvBXJG?#Ho+u8XSI3@&@Jo-MBdbfC1v@rH2$-$4^bm z4LRpUYb(SeS)_hbK)J$aziZs6wNbRjqLKF)MXf92>El9*n{QNgBPQkUTS6N9X9iI% zTbMz^_s=-?}Af7HS`FCS!a{ zGPPX|`meRqguSXsGg8AKnWotPgsft=UZPAry1tK!E(Aeo-%#J`Rr_)=Lb=5jf5wPB z7q@;!K9utA%L^FQ-ZeJ9t5tFN)v(@F!}pkumAk^Ew~JhKdWnZ1Urf~&MPlDKY)pNU zu32^C^2LyUwCq$AY2!g-S1$QC)2LxFf{{~xQfCKflIAdX-H=7eaxj0Wer0Id@$SjD zj^PL*lzy}lAJKi8cm^xa$xS@_%ol1|h6!mx%JmcWZ9YR^;ghqymwKX|PTYwd9(pWp zHI1d~9{T0&o&Q^^(dWrv*Ljh8^n%+{gze@~>s&X#*)OT z8grjX>W(X-u%(Q&t+7ZdYs(CSTrbU6Bw|n#Va0KKY)mJCZ3h~LK0afuHKVGiHL1>- z0v3@r)5G@?^F&oCc9sM+->YHAy!cpQi!-eF^PnU|0~!{eJ7xtQK!sR|#RI+Gl= z3wiG3(VJ;Z3QV-D7^+JgwlQ)Sb!okGpsI197YruRJtlW)Dm6(ZL@I(>#YOe(V;%-} zTpBL9SHVI+A1|Rh1~PEWN-W9A!W0>&%(j%|cqB)XBWH|$7`_U$lXb1W%D}D^#7ESq zLS6vZW;swQyHF%ZzFuuRqKvGA3o1p^uThz3dhh{u?ei$SdhcQZ7S1)cXIOsE9$wjS z&fS3Hf%Vw`>paw(t1g0yl~m#;-QT(!!qkXIS=MH>!KvyBw$Nj$kw?0N3$*h($*3dO zKyzp~shq)YtaO?R%AtqcVk9C8EAg)|!(fY$4XhsAr6|A*myuva*Jj1}U7$y@Z~)4=v>gnrfeAQd2`dZR-2uvwoftDnDuFItn+WqzMv1 z=$Zhc5~0|Eg}XIq^F+$q_~L|^HYh@-J2JH>&m>pqL5!%^R?0CwQ_^TozIP}SC`h%I z`vsynf;d4r4@)~HGWz04h(XM9T7gFRAks7;=- z!fl-mi9*&Zeh#Ll9Vp*w>BB|IMa2_=5$i$+4tdn@II(im>r)OiI3#R&tyCH){jsg=Fl1*+qG6oY3m&UvwM!uP%%#UXtl7W7F9KS{b9kxQJK( z&FCR`%z4jlWo@o3HQnY8Cs?v}c19e3*6PHlO9tLIyWX1Ra z;DEsjVA*0JyXcbv$7YYfDFZg1%Ah@}aw@X{ZYXkKhLN4Ag58SgMQ%*ag>iCTDdDCuB)0O6NBf`}w3Hwm^#@ahBAqW;<(+!}8cUx+JB3*2 zlAgqU<#08TldDEU%?bWapRiFB z&^b3UAnEy~O;}+WAdAG2ka5pOtdypKUKl=@K`37{x2wDP`)Iy{d{(qqI{ovp64= zrtsK8fpIH;gzRN3hSfTK(n0#UP0@-aR>=i%Z)Q1~!epkOIFdB~qA>MgM03KL#hj2{ zn-V75jFUqh2^B@}3EM@2Q{yg+x(<1tYoz2|B9-I(=sz{klo)7OEZiU z3AC{=+W|3`MJ-1XHy}U)VM4$2!g_U;W}5N`(uRfHPC_GEoyq78G0DjKCUCd6oJMf5 zQlqMwQB0dfBe{%F;ZWa;=rr&65ou37)z=kBQhh zWLtmopxaoYdz<6mxLU}}u}(CoXH;BfD~Xy!y;PF*;mmq_VUcBIs6PfXje7xQRyhVP zO{;q#zQ)$4KUR5^AW#fIc3L(*9Wic7BHv<3$*KEx!DD0pno5j21;wU5SU|0DB`vRN zX7k`hsS(s7qi{KyKMRj~0Er6GUg}Mx9v6^I;-&!c0^m0rQkf2062;8+Tj-_qIEvdQ zWpu}&Bu#5rYoT$W^*d9ZwV6_N3vV;is#_jsaEzOX?#x7k84<2*Fi+rMnR*$x>LR47 zsL|y?Gdr|qbgjH?bZMzSoI}`M@s8fo8FAJdL6ynYa577oh}|$;Tt}9xMWx^rC^E%5 zaILHIq-V@NYTpp$>d|srYa**TFY>(vnioX3t4TFJS!p?xi?qol6-ejSR&&uUYS#@# z>rh#HW=*TMfO$9c6N_rZHGNac2G((j_$7Wi;XwH>aX;@NLPu71Y z+ZTb*E64-^V+=NrR5Ca%+*9)O)p?uLZ01Dogmb%IMedkq^bw!+u-2kW{>HsVnF0dp z>1_Wtre(1%GCjvwOs!i>+o|g`1ql}4eLl3W;H3c#LPE%O23Rux{<#UmydQfT`Pkhq za1mL>vlj>UX{U$xd)~rZTd4`vAl!|3X;Qh-#vT}1c3p7`l`L$B=3Gne-i*I)mYXRN z#3p1Pq&p^iPIi|f=X0~(Zr)&~Y`h;U?}=U^s&^P-{J3=v%F2DqQVNF39TxHwQJvJH z*lIFD7zzca$?!=Voh0w*-0Tsp(QNJUQn9)Jjidu%dw*_sN?@Z8zyY}{`#^1?y{E0c ztB?-j(si9Tw+NdS>Ij1Dq-HKdXolCUs4T$FP+-V`ZfqD5?`4w6_;4q$U=5~Dr-6!$ z@tjMg0_5~ejDhlpve>7ont(_A>!GHo1WQc)lFM9ZD8zb>)Ku=pUlC<_qxVt!+dD|4@Rs?rU=*&&M?XkW5ApT&9GL&9{tdU#{>XR!XK|ZN9pN2g_i9{0HVnq8re~_xdm`Sh3+~icloaF!<_1 zdT8g4s$qOF%z~=I{ERAsh)0U3Iby5JJVpbzO&6dp2T?4$3Tk^o1NVI9&Roj}Q3r)u z27ah)oz5%ET0(BtCxBF=Y<_7_qKx3IZbt~r&_;~!SdMBY(k z3<(BVv5=@@cRo#Q=FcacQGd$JShg)`6pJitLHI7uvdTzpcuBtg&XcAMDzGlaeDBfj z(Np}e##~_v!yzLv4815U>p+CK)TyqXX7hp&b-=G{T5L|Tg&6}Zq1Ni_;KFwBGDCI(+C;ZruB%3aHcK%-lRfu?$dIM7?DVsCaa%t@B}Gp& zqYbzsaYD{TNazUK9?YarQ8q_m=>pLWRqthpC*;=RDq9KFvhc1nL;pl?uMjO&60Wag zBo8$UzZ6R@E(w^zkq$loiEA3_{hD&CUeGceQ@jLFjsmNhj!1lvN(c=OK0nUXoNE%T@x( zOcG5YWQm8qPeS&{mb~mOqowr1QXs3KPR6GQxg=}0PA3j%Jz9&C5G2ds2BAc7MRwFj zdhtrAP2SQ3Dt2UMZt56+tj&xKE8TAc7v=L^$phXRW=+eD_zq8&P$L;NGWHtEgl23dZ-*SgWB|3pocgIi^~+@(f*lpr z6VWjY6*RvJ?yva&Y*|cHGp)u^@}X856iC}oYwkqIuta(!!aJ-gO~+6C(lk)lG2hnd z2F>D0f2$Osh@TASEPqinHLunB6I@2)qY@0nbWuw#WDYBll*&OQ(qcJ5;(;J5COuF^ zy-PCgXOn_bxCRp>>2eI`=QD5ZGDmGrzpg2-4iQK44T%%Q9tJ(Di*bs_vCuR0NGP@{ zlrlk2gN$|lc4>%mufO~Wry|ob`3X=K&dK1lM|+|rV=r=CvNIC2;GW2I5~B*IO50$O z7srBQ`pE@3F+<@DAwNmOGz(;{bN|?~I;e#p3x{2g(wrt`3w8zZhigtRw7$uDz{uV_ujcm*=yC{~k)S+B7z z)2(Lb;ZHLW^Bm2vwz1!wlZfhxS^KbofP=t*PgV0kPfjm6BQ2En2;o|@Eh1_PMJxUq zWjen^w>b1hBISG*L?b!H|IUm#l+srAPjwtYx$gIA`7i;rtBpE7cG!1_(G&S-^nY2)o_?YRalcwtAlB{soM}mwd2C?-j3hay($*#2Pw3P}i_r>6}_tsDQXv%J? zbU$(bNXB?H&|Xb_$*=U-%lhUiI!&vyIMimFu6PG)b`%z1TnDq9S5g{B9t(AFTPKZ5 zX%vHdC2n{X~d>4#fU*rRlr2fz*|EkYu$ZBz7 zb2qL@7`LWFz3dJb_^f_5VHC+opwb$>P99hX<$Tg{Xi!)Wbqm=r&X#DU3Ro<)_+woE z)^tyYBk!v4ekm@d7CVkHCFiZYZWdpaZB+A=K2wq@hblxY@gP(SfbZsN8_b2h2~vcT zZXL3PgY$!YxUf*^EX>%&60!~%lIgzlR1q*EQ!NEWGAM=cd}V7jpCy@-6FW~+etMNl zZMWXCagciP=qxryA}nBG;&al=Z{jY%9x>)@PCytmeu@TTn0FM*&)R-j442JquP*Gm zG;ED*GN(nOb~Zz;$`6RnjoJiF z<+@CP_vA^)5p*Ylxr}x*{OJj2&3^J`MI_6l-4Xe2>w)=;i~tL3-R?ehc#S>(OGFNf zjG_?rx>ucLle>^`!ZIeD=5An=XqS_iGK(k!y97`_H(IX23H2})aSyFMkzzYX*3$53z|uC(NHDKV$h^w+e^D04 zmQ8H;tR+3~F^8gm9jn+naa9(<05MLcQiKa3=%F8$NNojcxb_cGBx0HW17qY?B@woQ zh|MhU=|?o@bkWs`Z|RA{q`uD&A&<$TQF?6*=sPjEV+Ds#B}}{Aa%kvVVan}Hf_sOt zk+wSRb3rGwX^nTjCs2}9fOlza7bLN9X%rjHWY@P>UiP)C%*)mjcollK_)c11l?{DZ zQS?gpF2%pLi)wyPkr8T7w?hT*G#JbF3qp*D9aZ0Uj8uKWdz7+SF=@YpmU2u{;;N28 zkPUu4_LCC}X5jMtdT`tjT63UzKr{SKLHYmCuQ1;QmHFBifC3r8@!w>FRr}g*$GnR| zy2i1@UjYR5Xop|$`HI3B&9e7xS|-~z>}`5Zxub{)-zs z@JwCV(8c8qOVC~-(hc~X=rFsftpW^W>aUW&SlQ(6LD8V<}$=Gv@B>$Q8l zN@}f-oUFEu^Q5lQs>j%w>{6~7I6+`H)R1PN7FM27r7E5@mqcj79rq5eQwhB8RVeyH zm9DH9If@)Nsw2j&MGWW|i5h2xe4ZAR{h=hK)tz6ogh{FIK<&wd4~bXzOKm%*mi1s4 zIR<#Gd9TQMJv;fRk=0dw#ABV74}ECd)aZopN-bT^$oo&`7MV*{Jr5d;t>#3fC{dgH pve;^`$V#Ia?NI79JW{yUFgB$>Om&Y8+?P-!r%Y^ literal 0 HcmV?d00001 diff --git a/static/img/player-sprite.png b/static/img/player-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e7038115005be0348e37796a3661b9e8298629 GIT binary patch literal 8325 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 W_}I54@c6^48=7jm4@&Mo4f!ALv!` +(Backbone, Marionette, DssRouter, PanningRegion, AudioController, HeaderView, SidebarView, MixCollection) -> Marionette.Region.prototype.open = (view) -> @.$el.hide(); @.$el.html(view.el); @@ -8,6 +8,7 @@ define ['backbone', 'marionette', 'app.lib/router', 'app.lib/panningRegion', 'vi true App = new Marionette.Application(); + App.audioController = new AudioController() App.vent.on "mix:favourite", (model) -> console.log "App(vent): mix:favourite" @@ -50,7 +51,7 @@ define ['backbone', 'marionette', 'app.lib/router', 'app.lib/panningRegion', 'vi App.addInitializer -> console.log("App: routing starting"); App.Router = new DssRouter(); - return App.vent.trigger("routing:started"); + App.vent.trigger("routing:started"); App.addInitializer -> console.log("App: gobbling links"); diff --git a/static/js/app/appv2.js b/static/js/app/appv2.js index e62d1dc..9b8413b 100644 --- a/static/js/app/appv2.js +++ b/static/js/app/appv2.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 1.6.2 (function() { - define(['backbone', 'marionette', 'app.lib/router', 'app.lib/panningRegion', 'views/header', 'views/sidebar/sidebarView', 'models/mix/mixCollection'], function(Backbone, Marionette, DssRouter, PanningRegion, HeaderView, SidebarView, MixCollection) { + 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) { @@ -10,6 +10,7 @@ return true; }; App = new Marionette.Application(); + App.audioController = new AudioController(); App.vent.on("mix:favourite", function(model) { console.log("App(vent): mix:favourite"); model.save('favourited', !model.get('favourited'), { diff --git a/static/js/app/dss.bootstrapper.js b/static/js/app/dss.bootstrapper.js index f442f19..0e8190f 100644 --- a/static/js/app/dss.bootstrapper.js +++ b/static/js/app/dss.bootstrapper.js @@ -3,12 +3,15 @@ requirejs.config({ paths: { jquery: 'libs/jquery', backbone: 'libs/backbone/backbone', + 'backbone.babysitter': 'libs/backbone/backbone.babysitter', marionette: 'libs/backbone/backbone.marionette', + 'backbone.wreqr': 'libs/backbone/backbone.wreqr', ich: 'libs/ICanHaz', underscore: 'libs/backbone/underscore', text: 'libs/text', templates: '/templates', app: 'app/appv2', + vent: 'app/lib/eventAggregator', views: 'app/views', models: 'app/models', 'app.lib': 'app/lib', @@ -27,15 +30,15 @@ requirejs.config({ underscore: { exports: '_' }, - 'toastr': { - deps: ['jquery'], - exports: 'toastr' - } + 'toastr': { + deps: ['jquery'], + exports: 'toastr' + } } }); requirejs(['toastr', 'underscore', 'backbone', 'app'], function (toastr, _, Backbone, App) { - "require strict" + "use strict" console.log("Dss.Bootstrapper: primed"); App.start(); diff --git a/static/js/app/lib/audioController.coffee b/static/js/app/lib/audioController.coffee new file mode 100644 index 0000000..0134537 --- /dev/null +++ b/static/js/app/lib/audioController.coffee @@ -0,0 +1,35 @@ +define ['app', 'marionette', 'vent'], +(App, Marionette, vent) -> + class AudioController extends Marionette.Controller + + initialize: (options) -> + console.log "AudioController: initialize" + @listenTo(vent, 'mix:init', @mixInit) + @listenTo(vent, 'mix:pause', @mixPause) + @listenTo(vent, 'mix:play', @mixPlay) + + mixInit: (model) => + console.log "AudioController: mixInit" + id = model.get('id') + $.getJSON "/ajax/mix_stream_url/" + id + "/", (data) => + com.podnoms.settings.setupPlayerWrapper(id, data.stream_url) + com.podnoms.player.startPlaying + success: => + vent.trigger("mix:play", model) + com.podnoms.utils.checkPlayCount() + return + error: => + com.podnoms.utils.showWarning "Ooops", "Error playing mix. If you have a flash blocker, please disable it for this site. Otherwise, do please try again." + return + com.podnoms.storage.setItem "now_playing", id + return + + mixPlay: (model) -> + console.log("AudioController: mixPlay") + com.podnoms.player.resume(); + + mixPause: (model) -> + console.log("AudioController: mixPause") + com.podnoms.player.pause(); + AudioController + diff --git a/static/js/app/lib/audioController.js b/static/js/app/lib/audioController.js new file mode 100644 index 0000000..ff59f7c --- /dev/null +++ b/static/js/app/lib/audioController.js @@ -0,0 +1,62 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + define(['app', 'marionette', 'vent'], function(App, Marionette, vent) { + var AudioController, _ref; + + AudioController = (function(_super) { + __extends(AudioController, _super); + + function AudioController() { + this.mixInit = __bind(this.mixInit, this); _ref = AudioController.__super__.constructor.apply(this, arguments); + return _ref; + } + + AudioController.prototype.initialize = function(options) { + console.log("AudioController: initialize"); + this.listenTo(vent, 'mix:init', this.mixInit); + this.listenTo(vent, 'mix:pause', this.mixPause); + return this.listenTo(vent, 'mix:play', this.mixPlay); + }; + + AudioController.prototype.mixInit = function(model) { + var id, + _this = this; + + console.log("AudioController: mixInit"); + id = model.get('id'); + return $.getJSON("/ajax/mix_stream_url/" + id + "/", function(data) { + com.podnoms.settings.setupPlayerWrapper(id, data.stream_url); + com.podnoms.player.startPlaying({ + success: function() { + vent.trigger("mix:play", model); + com.podnoms.utils.checkPlayCount(); + }, + error: function() { + com.podnoms.utils.showWarning("Ooops", "Error playing mix. If you have a flash blocker, please disable it for this site. Otherwise, do please try again."); + } + }); + com.podnoms.storage.setItem("now_playing", id); + }); + }; + + AudioController.prototype.mixPlay = function(model) { + console.log("AudioController: mixPlay"); + return com.podnoms.player.resume(); + }; + + AudioController.prototype.mixPause = function(model) { + console.log("AudioController: mixPause"); + return com.podnoms.player.pause(); + }; + + return AudioController; + + })(Marionette.Controller); + return AudioController; + }); + +}).call(this); diff --git a/static/js/app/lib/eventAggregator.coffee b/static/js/app/lib/eventAggregator.coffee index 919c700..1229714 100644 --- a/static/js/app/lib/eventAggregator.coffee +++ b/static/js/app/lib/eventAggregator.coffee @@ -1,6 +1,3 @@ -define ['marionette'], -(Marionette) -> - class EventAggregator extends Marionette.EventAggregator +define ['backbone.wreqr'], (Wreqr) -> + new Wreqr.EventAggregator - - EventAggregator \ No newline at end of file diff --git a/static/js/app/lib/eventAggregator.js b/static/js/app/lib/eventAggregator.js index d9dc866..55fe2e5 100644 --- a/static/js/app/lib/eventAggregator.js +++ b/static/js/app/lib/eventAggregator.js @@ -1,22 +1,7 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { - var __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - define(['marionette'], function(Marionette) { - var EventAggregator; - EventAggregator = (function(_super) { - - __extends(EventAggregator, _super); - - function EventAggregator() { - return EventAggregator.__super__.constructor.apply(this, arguments); - } - - return EventAggregator; - - })(Marionette.EventAggregator); - return EventAggregator; + define(['backbone.wreqr'], function(Wreqr) { + return new Wreqr.EventAggregator; }); }).call(this); diff --git a/static/js/app/models/user/userCollection.js b/static/js/app/models/user/userCollection.js index 9db6a7e..29367d8 100644 --- a/static/js/app/models/user/userCollection.js +++ b/static/js/app/models/user/userCollection.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(['backbone', 'models/user/userItem', 'app.lib/backbone.dss.model.collection'], function(Backbone, UserItem, DssCollection) { - var UserCollection; - UserCollection = (function(_super) { + var UserCollection, _ref; + UserCollection = (function(_super) { __extends(UserCollection, _super); function UserCollection() { - return UserCollection.__super__.constructor.apply(this, arguments); + _ref = UserCollection.__super__.constructor.apply(this, arguments); + return _ref; } UserCollection.prototype.model = UserItem; diff --git a/static/js/app/views/mix/mixItemView.coffee b/static/js/app/views/mix/mixItemView.coffee index 02f7347..e00d5cd 100644 --- a/static/js/app/views/mix/mixItemView.coffee +++ b/static/js/app/views/mix/mixItemView.coffee @@ -1,27 +1,32 @@ -define ['moment', 'app', 'marionette', 'models/comment/commentCollection', 'views/comment/commentListView', 'text!/tpl/MixListItemView'], -(moment, App, Marionette, CommentsCollection, CommentsListView, Template) -> +define ['moment', 'app', 'vent', 'marionette', 'models/comment/commentCollection', 'views/comment/commentListView', 'text!/tpl/MixListItemView'], +(moment, App, vent, Marionette, CommentsCollection, CommentsListView, Template) -> class MixItemView extends Marionette.ItemView template: _.template(Template) tagName: @tagName or "li" className: @className or "" events: { - "click .play-button-small-start": "startMix", - "click .play-button-small-resume": "resumeMix", - "click .play-button-small-pause": "pauseMix", + "click .play-button-small-start": "doStart", + "click .play-button-small-resume": "doResume", + "click .play-button-small-pause": "doPause", "click .mix-link": "mixLink", - "click .like-button a": "likeMix", - "click .favourite-button a": "favouriteMix", - "click .share-button": "shareMix", - "click .download-button a": "downloadMix" + "click .like-button a": "mixLike", + "click .favourite-button a": "mixFavourite", + "click .share-button": "mixShare", + "click .download-button a": "mixDownload" + } + + ui: { + playButton: ".play-button-small" } initialize: => @listenTo(@model, 'change:favourited', @render) @listenTo(@model, 'change:liked', @render) + @listenTo(vent, 'mix:play', @mixPlay) + @listenTo(vent, 'mix:pause', @mixPause) true - onRender: => id = @model.get('id') if @model.get('duration') @@ -31,40 +36,11 @@ define ['moment', 'app', 'marionette', 'models/comment/commentCollection', 'view #check if we're currently playing if com.podnoms.player.isPlayingId @model.id - com.podnoms.settings.setupPlayer @model.toJSON() + com.podnoms.settings.setupPlayerWrapper @model.get('id') @renderGenres() return - startMix: => - console.log("MixItemView: starting mix") - id = @model.get('id') - $.getJSON "/ajax/mix_stream_url/" + id + "/", (data) -> - com.podnoms.settings.setupPlayer(data, id) - com.podnoms.player.startPlaying - success: -> - window._eventAggregator.trigger "track_playing" - window._eventAggregator.trigger "track_changed", data - com.podnoms.utils.checkPlayCount() - return - error: -> - com.podnoms.utils.showWarning "Ooops", "Error playing mix. If you have a flash blocker, please disable it for this site. Otherwise, do please try again." - - com.podnoms.storage.setItem "now_playing", id - return - - pauseMix: -> - console.log("MixItemView: pauseMix") - com.podnoms.player.pause(); - @.trigger("mix:paused", @model); - true - - resumeMix: -> - console.log("MixItemView: resumeMix") - com.podnoms.player.resume(); - @trigger("mix:resumed", @model); - true - renderGenres: => el = @el $.each @model.get("genre-list"), (data) -> @@ -84,19 +60,55 @@ define ['moment', 'app', 'marionette', 'models/comment/commentCollection', 'view true true - favouriteMix: -> + doStart: => + console.log("MixItemView: mixStart") + this.ui.playButton + .toggleClass('play-button-small-start', false) + .toggleClass('play-button-small-resume', false) + .toggleClass('play-button-small-pause', true) + + vent.trigger('mix:init', @model) + return + + doPause: -> + console.log("MixItemView: mixPause") + vent.trigger("mix:pause", @model); + true + + doResume: -> + console.log("MixItemView: mixResume") + vent.trigger("mix:play", @model); + true + + mixPlay: (model) -> + if (@model.get('id') == model.get('id')) + this.ui.playButton + .toggleClass('play-button-small-start', false) + .toggleClass('play-button-small-resume', false) + .toggleClass('play-button-small-pause', true) + return + + mixPause: (model) -> + if (@model.get('id') == model.get('id')) + this.ui.playButton + .toggleClass('play-button-small-start', false) + .toggleClass('play-button-small-resume', true) + .toggleClass('play-button-small-pause', false) + return + + mixFavourite: -> console.log("MixItemView: favouriteMix") app = require('app') app.vent.trigger("mix:favourite", @model) true - likeMix: -> + mixLike: -> console.log("MixItemView: likeMix") app = require('app') app.vent.trigger("mix:like", @model) true - shareMix: (e) -> + mixShare: (e) -> console.log("MixItemView: shareMix") mode = $(e.currentTarget).data("mode"); console.log("MixItemView: "+ mode) diff --git a/static/js/app/views/mix/mixItemView.js b/static/js/app/views/mix/mixItemView.js index b8759be..2e14c4b 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', 'marionette', 'models/comment/commentCollection', 'views/comment/commentListView', 'text!/tpl/MixListItemView'], function(moment, App, Marionette, CommentsCollection, CommentsListView, Template) { - var MixItemView; - MixItemView = (function(_super) { + define(['moment', 'app', 'vent', 'marionette', 'models/comment/commentCollection', 'views/comment/commentListView', 'text!/tpl/MixListItemView'], function(moment, App, vent, Marionette, CommentsCollection, CommentsListView, Template) { + 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.startMix = __bind(this.startMix, 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); @@ -30,24 +26,31 @@ MixItemView.prototype.className = MixItemView.className || ""; MixItemView.prototype.events = { - "click .play-button-small-start": "startMix", - "click .play-button-small-resume": "resumeMix", - "click .play-button-small-pause": "pauseMix", + "click .play-button-small-start": "doStart", + "click .play-button-small-resume": "doResume", + "click .play-button-small-pause": "doPause", "click .mix-link": "mixLink", - "click .like-button a": "likeMix", - "click .favourite-button a": "favouriteMix", - "click .share-button": "shareMix", - "click .download-button a": "downloadMix" + "click .like-button a": "mixLike", + "click .favourite-button a": "mixFavourite", + "click .share-button": "mixShare", + "click .download-button a": "mixDownload" + }; + + MixItemView.prototype.ui = { + playButton: ".play-button-small" }; MixItemView.prototype.initialize = function() { this.listenTo(this.model, 'change:favourited', this.render); this.listenTo(this.model, 'change:liked', this.render); + this.listenTo(vent, 'mix:play', this.mixPlay); + this.listenTo(vent, 'mix:pause', this.mixPause); return true; }; 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"); @@ -55,47 +58,14 @@ $('#player-duration-' + id, this.el).text(totalDurationText); } if (com.podnoms.player.isPlayingId(this.model.id)) { - com.podnoms.settings.setupPlayer(this.model.toJSON()); + com.podnoms.settings.setupPlayerWrapper(this.model.get('id')); } this.renderGenres(); }; - MixItemView.prototype.startMix = function() { - var id; - console.log("MixItemView: starting mix"); - id = this.model.get('id'); - $.getJSON("/ajax/mix_stream_url/" + id + "/", function(data) { - com.podnoms.settings.setupPlayer(data, id); - com.podnoms.player.startPlaying({ - success: function() { - window._eventAggregator.trigger("track_playing"); - window._eventAggregator.trigger("track_changed", data); - com.podnoms.utils.checkPlayCount(); - }, - error: function() { - return com.podnoms.utils.showWarning("Ooops", "Error playing mix. If you have a flash blocker, please disable it for this site. Otherwise, do please try again."); - } - }); - return com.podnoms.storage.setItem("now_playing", id); - }); - }; - - MixItemView.prototype.pauseMix = function() { - console.log("MixItemView: pauseMix"); - com.podnoms.player.pause(); - this.trigger("mix:paused", this.model); - return true; - }; - - MixItemView.prototype.resumeMix = function() { - console.log("MixItemView: resumeMix"); - com.podnoms.player.resume(); - this.trigger("mix:resumed", this.model); - 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 + ''); @@ -106,6 +76,7 @@ MixItemView.prototype.renderComments = function() { var comments; + comments = new CommentsCollection(); comments.url = this.model.get("resource_uri") + "comments/"; comments.mix_id = this.model.id; @@ -113,6 +84,7 @@ comments.fetch({ success: function(data) { var content; + console.log(data); content = new CommentsListView({ collection: comments @@ -124,24 +96,57 @@ return true; }; - MixItemView.prototype.favouriteMix = function() { + MixItemView.prototype.doStart = function() { + console.log("MixItemView: mixStart"); + this.ui.playButton.toggleClass('play-button-small-start', false).toggleClass('play-button-small-resume', false).toggleClass('play-button-small-pause', true); + vent.trigger('mix:init', this.model); + }; + + MixItemView.prototype.doPause = function() { + console.log("MixItemView: mixPause"); + vent.trigger("mix:pause", this.model); + return true; + }; + + MixItemView.prototype.doResume = function() { + console.log("MixItemView: mixResume"); + vent.trigger("mix:play", this.model); + return true; + }; + + MixItemView.prototype.mixPlay = function(model) { + if (this.model.get('id') === model.get('id')) { + this.ui.playButton.toggleClass('play-button-small-start', false).toggleClass('play-button-small-resume', false).toggleClass('play-button-small-pause', true); + } + }; + + MixItemView.prototype.mixPause = function(model) { + if (this.model.get('id') === model.get('id')) { + this.ui.playButton.toggleClass('play-button-small-start', false).toggleClass('play-button-small-resume', true).toggleClass('play-button-small-pause', false); + } + }; + + MixItemView.prototype.mixFavourite = function() { var app; + console.log("MixItemView: favouriteMix"); app = require('app'); app.vent.trigger("mix:favourite", this.model); return true; }; - MixItemView.prototype.likeMix = function() { + MixItemView.prototype.mixLike = function() { var app; + console.log("MixItemView: likeMix"); app = require('app'); app.vent.trigger("mix:like", this.model); return true; }; - MixItemView.prototype.shareMix = function(e) { + MixItemView.prototype.mixShare = function(e) { var app, mode; + console.log("MixItemView: shareMix"); mode = $(e.currentTarget).data("mode"); console.log("MixItemView: " + mode); diff --git a/static/js/app/views/mix/mixListView.coffee b/static/js/app/views/mix/mixListView.coffee index b7fe6ba..45e8015 100644 --- a/static/js/app/views/mix/mixListView.coffee +++ b/static/js/app/views/mix/mixListView.coffee @@ -1,5 +1,5 @@ -define ['marionette', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text!/tpl/MixListView'], -(Marionette, MixCollection, MixItemView, Template) -> +define ['marionette', 'vent', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text!/tpl/MixListView'], +(Marionette, vent, MixCollection, MixItemView, Template) -> class MixListView extends Marionette.CompositeView template: _.template(Template) @@ -7,6 +7,8 @@ define ['marionette', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text itemView: MixItemView itemViewContainer: "#mix-list-container-ul" + currentMix = -1 + initialize: -> console.log "MixListView: initialize" @collection = new MixCollection() @@ -15,10 +17,21 @@ define ['marionette', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text success: => console.log("MixListView: Collection fetched") @tabChanged('latest') + @listenTo(vent, 'mix:play', @mixPlay) true ) return + mixPlay: (model) -> + console.log "MixListView: mixPlay" + """ + if currentMix != -1 + v = @children.findByModelCid(currentMix) + v.mixPause() + """ + currentMix = model.cid + return + onRender: -> $('#li-' + @options.type, @el).addClass('active') true diff --git a/static/js/app/views/mix/mixListView.js b/static/js/app/views/mix/mixListView.js index cbb0652..2bc1d4c 100644 --- a/static/js/app/views/mix/mixListView.js +++ b/static/js/app/views/mix/mixListView.js @@ -3,10 +3,12 @@ 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/mixCollection', 'views/mix/mixItemView', 'text!/tpl/MixListView'], function(Marionette, MixCollection, MixItemView, Template) { + define(['marionette', 'vent', 'models/mix/mixCollection', 'views/mix/mixItemView', 'text!/tpl/MixListView'], function(Marionette, vent, MixCollection, MixItemView, Template) { var MixListView, _ref; MixListView = (function(_super) { + var currentMix; + __extends(MixListView, _super); function MixListView() { @@ -22,6 +24,8 @@ MixListView.prototype.itemViewContainer = "#mix-list-container-ul"; + currentMix = -1; + MixListView.prototype.initialize = function() { var _this = this; @@ -32,11 +36,18 @@ success: function() { console.log("MixListView: Collection fetched"); _this.tabChanged('latest'); + _this.listenTo(vent, 'mix:play', _this.mixPlay); return true; } }); }; + MixListView.prototype.mixPlay = function(model) { + console.log("MixListView: mixPlay"); + "if currentMix != -1\n v = @children.findByModelCid(currentMix)\n v.mixPause()"; + currentMix = model.cid; + }; + MixListView.prototype.onRender = function() { $('#li-' + this.options.type, this.el).addClass('active'); return true; diff --git a/static/js/app/views/sidebar/sidebarView.coffee b/static/js/app/views/sidebar/sidebarView.coffee index ff89965..ef09405 100644 --- a/static/js/app/views/sidebar/sidebarView.coffee +++ b/static/js/app/views/sidebar/sidebarView.coffee @@ -1,19 +1,42 @@ -define ['underscore', 'backbone', 'marionette', 'views/activity/activityListView', 'text!/tpl/SidebarView'], -(_, Backbone, Marionette, ActivityListView, Template) -> +define ['underscore', 'backbone', 'marionette', 'vent', 'views/activity/activityListView', 'views/widgets/nowPlayingView', 'text!/tpl/SidebarView'], +(_, Backbone, Marionette, vent, ActivityListView, NowPlayingView, Template) -> + class SidebarView extends Marionette.Layout template: _.template(Template) - className: "tabbable" regions: topRegion: '#sidebar-top-content' streamRegion: '#sidebar-stream-content' + initialize: -> console.log "SidebarView: initialize" + this.listenTo(vent, 'mix:init', @mixInit) + this.listenTo(vent, 'mix:play', @mixPlay) + this.listenTo(vent, 'mix:pause', @mixPause) + return + + onRender: -> + console.log "SidebarView: onRender" return onShow: -> console.log "SidebarView: onShow" - #this.topRegion.show(new NowPlayingView()) - this.streamRegion.show(new ActivityListView()) + @streamRegion.show(new ActivityListView()) + $(@topRegion.el).hide() + """ + @topRegion.show( + new NowPlayingView( + model: new Backbone.Model({ + item_url: "fdskjfhdsk", title: "Argle bargle", user_profile_url: "/", user_name: "Foo Ferra" + }) + )) + """ return + mixInit: (model) -> + console.log "SidebarView: mixInit" + $(@topRegion.el).show() + @topRegion.show(new NowPlayingView({model: model})) + + SidebarView + diff --git a/static/js/app/views/sidebar/sidebarView.js b/static/js/app/views/sidebar/sidebarView.js index f43bad5..0aa6aa6 100644 --- a/static/js/app/views/sidebar/sidebarView.js +++ b/static/js/app/views/sidebar/sidebarView.js @@ -1,22 +1,21 @@ -// Generated by CoffeeScript 1.3.3 +// Generated by CoffeeScript 1.6.2 (function() { var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - define(['underscore', 'backbone', 'marionette', 'views/activity/activityListView', 'text!/tpl/SidebarView'], function(_, Backbone, Marionette, ActivityListView, Template) { - var SidebarView; - return SidebarView = (function(_super) { + define(['underscore', 'backbone', 'marionette', 'vent', 'views/activity/activityListView', 'views/widgets/nowPlayingView', 'text!/tpl/SidebarView'], function(_, Backbone, Marionette, vent, ActivityListView, NowPlayingView, Template) { + var SidebarView, _ref; + SidebarView = (function(_super) { __extends(SidebarView, _super); function SidebarView() { - return SidebarView.__super__.constructor.apply(this, arguments); + _ref = SidebarView.__super__.constructor.apply(this, arguments); + return _ref; } SidebarView.prototype.template = _.template(Template); - SidebarView.prototype.className = "tabbable"; - SidebarView.prototype.regions = { topRegion: '#sidebar-top-content', streamRegion: '#sidebar-stream-content' @@ -24,16 +23,34 @@ SidebarView.prototype.initialize = function() { console.log("SidebarView: initialize"); + this.listenTo(vent, 'mix:init', this.mixInit); + this.listenTo(vent, 'mix:play', this.mixPlay); + this.listenTo(vent, 'mix:pause', this.mixPause); + }; + + SidebarView.prototype.onRender = function() { + console.log("SidebarView: onRender"); }; SidebarView.prototype.onShow = function() { console.log("SidebarView: onShow"); this.streamRegion.show(new ActivityListView()); + $(this.topRegion.el).hide(); + "@topRegion.show(\n new NowPlayingView(\n model: new Backbone.Model({\n item_url: \"fdskjfhdsk\", title: \"Argle bargle\", user_profile_url: \"/\", user_name: \"Foo Ferra\"\n })\n ))"; + }; + + SidebarView.prototype.mixInit = function(model) { + console.log("SidebarView: mixInit"); + $(this.topRegion.el).show(); + return this.topRegion.show(new NowPlayingView({ + model: model + })); }; return SidebarView; })(Marionette.Layout); + return SidebarView; }); }).call(this); diff --git a/static/js/app/views/user/userListView.coffee b/static/js/app/views/user/userListView.coffee index f18d9a9..e4b3d2a 100644 --- a/static/js/app/views/user/userListView.coffee +++ b/static/js/app/views/user/userListView.coffee @@ -1,4 +1,4 @@ -define ['marionette', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView'], +define ['marionette', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView', 'libs/jquery.dataTables'], (Marionette, UserCollection, UserItemView, Template) -> class UserListView extends Marionette.CompositeView @@ -14,7 +14,7 @@ define ['marionette', 'models/user/userCollection', 'views/user/userItemView', ' data: @options success: => console.log("UserListView: Collection fetched") - $(@el).tablesorter() + $(@el).dataTable sDom: "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>" return ) return diff --git a/static/js/app/views/user/userListView.js b/static/js/app/views/user/userListView.js index 8ac0fdb..6a26191 100644 --- a/static/js/app/views/user/userListView.js +++ b/static/js/app/views/user/userListView.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/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView'], function(Marionette, UserCollection, UserItemView, Template) { - var UserListView; - UserListView = (function(_super) { + define(['marionette', 'models/user/userCollection', 'views/user/userItemView', 'text!/tpl/UserListView', 'libs/jquery.dataTables'], function(Marionette, UserCollection, UserItemView, Template) { + var UserListView, _ref; + UserListView = (function(_super) { __extends(UserListView, _super); function UserListView() { - return UserListView.__super__.constructor.apply(this, arguments); + _ref = UserListView.__super__.constructor.apply(this, arguments); + return _ref; } UserListView.prototype.template = _.template(Template); @@ -23,13 +24,16 @@ UserListView.prototype.initialize = function() { var _this = this; + console.log("UserListView: initialize"); this.collection = new UserCollection(); this.collection.fetch({ data: this.options, success: function() { console.log("UserListView: Collection fetched"); - $(_this.el).tablesorter(); + $(_this.el).dataTable({ + sDom: "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>" + }); } }); }; diff --git a/static/js/app/views/widgets/nowPlayingView.coffee b/static/js/app/views/widgets/nowPlayingView.coffee new file mode 100644 index 0000000..8c19e8f --- /dev/null +++ b/static/js/app/views/widgets/nowPlayingView.coffee @@ -0,0 +1,48 @@ +define ['marionette', 'vent', 'text!/tpl/NowPlayingView'], +(Marionette, vent, Template) -> + + class NowPlayingView extends Marionette.ItemView + template: _.template(Template) + className: "now-playing" + + events: { + "click .now-playing-play": "doPlay", + "click .now-playing-pause": "doPause" + } + + initialize: -> + console.log "NowPlayingView: initialize" + @listenTo(vent, 'mix:play', @mixPlay) + @listenTo(vent, 'mix:pause', @mixPause) + true + + onRender: -> + @mixPlay() + true + + mixPause: (model) -> + console.log "NowPlayingView: mixPause" + $('#now-playing-playing-toggle', @el) + .toggleClass('now-playing-play', true) + .toggleClass('now-playing-pause', false) + true + + mixPlay: (model) -> + console.log "NowPlayingView: mixPlay" + $('#now-playing-playing-toggle', @el) + .toggleClass('now-playing-play', false) + .toggleClass('now-playing-pause', true) + true + + doPlay: -> + console.log "NowPlayingView: doPlay" + vent.trigger('mix:play', @model) + true + + doPause: -> + console.log "NowPlayingView: doPause" + vent.trigger('mix:pause', @model) + true + + NowPlayingView + diff --git a/static/js/app/views/widgets/nowPlayingView.js b/static/js/app/views/widgets/nowPlayingView.js new file mode 100644 index 0000000..53c0212 --- /dev/null +++ b/static/js/app/views/widgets/nowPlayingView.js @@ -0,0 +1,68 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + define(['marionette', 'vent', 'text!/tpl/NowPlayingView'], function(Marionette, vent, Template) { + var NowPlayingView, _ref; + + NowPlayingView = (function(_super) { + __extends(NowPlayingView, _super); + + function NowPlayingView() { + _ref = NowPlayingView.__super__.constructor.apply(this, arguments); + return _ref; + } + + NowPlayingView.prototype.template = _.template(Template); + + NowPlayingView.prototype.className = "now-playing"; + + NowPlayingView.prototype.events = { + "click .now-playing-play": "doPlay", + "click .now-playing-pause": "doPause" + }; + + NowPlayingView.prototype.initialize = function() { + console.log("NowPlayingView: initialize"); + this.listenTo(vent, 'mix:play', this.mixPlay); + this.listenTo(vent, 'mix:pause', this.mixPause); + return true; + }; + + NowPlayingView.prototype.onRender = function() { + this.mixPlay(); + return true; + }; + + NowPlayingView.prototype.mixPause = function(model) { + console.log("NowPlayingView: mixPause"); + $('#now-playing-playing-toggle', this.el).toggleClass('now-playing-play', true).toggleClass('now-playing-pause', false); + return true; + }; + + NowPlayingView.prototype.mixPlay = function(model) { + console.log("NowPlayingView: mixPlay"); + $('#now-playing-playing-toggle', this.el).toggleClass('now-playing-play', false).toggleClass('now-playing-pause', true); + return true; + }; + + NowPlayingView.prototype.doPlay = function() { + console.log("NowPlayingView: doPlay"); + vent.trigger('mix:play', this.model); + return true; + }; + + NowPlayingView.prototype.doPause = function() { + console.log("NowPlayingView: doPause"); + vent.trigger('mix:pause', this.model); + return true; + }; + + return NowPlayingView; + + })(Marionette.ItemView); + return NowPlayingView; + }); + +}).call(this); diff --git a/static/js/com.podnoms.player.js b/static/js/com.podnoms.player.js index 845b8fc..a9c6b64 100644 --- a/static/js/com.podnoms.player.js +++ b/static/js/com.podnoms.player.js @@ -58,9 +58,6 @@ com.podnoms.player = { }, _whilePlaying: function () { if (!this.trackLoaded) { - this.playButtonEl - .removeClass('play-button-small-loading') - .addClass('play-button-small-pause'); this.trackLoaded = true; } this.currentPosition = this.currentSound.position; @@ -93,12 +90,6 @@ com.podnoms.player = { soundManager.destroySound(this.currentSound.sID); } this.trackLoaded = false; - if (this.playButtonEl != undefined) - this.playButtonEl - .removeClass('play-button-small-pause') - .removeClass('play-button-small-loading') - .addClass('play-button-small-start'); - this.currentId = null; if (success != undefined) success(); @@ -110,7 +101,6 @@ com.podnoms.player = { this.seekHeadEl = options.seekHeadEl; this.playHeadEl = options.playHeadEl; this.loadingEl = options.loadingEl; - this.playButtonEl = options.playButtonEl; this.currentPath = options.url; }, _setupParams: function () { @@ -151,13 +141,6 @@ com.podnoms.player = { setupPlayer: function (options) { this._parseOptions(options); this._setupParams(); - if (this.isPlayingId(options.id)) { - this.playButtonEl - .removeClass('play-button-small-start') - .removeClass('play-button-small-loading') - .addClass('play-button-small-pause'); - } - }, startPlaying: function (options) { var ref = this; @@ -219,21 +202,12 @@ com.podnoms.player = { play: function () { this.currentSound.play(); - this.playButtonEl - .removeClass('play-button-small-start') - .addClass('play-button-small-loading'); }, pause: function () { this.currentSound.pause(); - this.playButtonEl - .removeClass('play-button-small-pause') - .addClass('play-button-small-resume'); }, resume: function () { this.currentSound.resume(); - this.playButtonEl - .removeClass('play-button-small-resume') - .addClass('play-button-small-pause'); }, forward: function (increment) { diff --git a/static/js/com.podnoms.utils.js b/static/js/com.podnoms.utils.js index 1a44abe..9347fe4 100644 --- a/static/js/com.podnoms.utils.js +++ b/static/js/com.podnoms.utils.js @@ -109,7 +109,7 @@ com.podnoms.utils = { if (document.cookie.indexOf('sessionId')) { $.getJSON('/ajax/session_play_count', function (data) { if ((data.play_count != 0) && (data.play_count % 5) == 0) { - com.podnoms.utils.modal('tpl/PlayCountLoginAlert'); + com.podnoms.utils.showAlert('tpl/PlayCountLoginAlert'); } }); } diff --git a/static/js/libs/backbone/backbone.babysitter.js b/static/js/libs/backbone/backbone.babysitter.js new file mode 100644 index 0000000..235410b --- /dev/null +++ b/static/js/libs/backbone/backbone.babysitter.js @@ -0,0 +1,177 @@ +// Backbone.BabySitter +// ------------------- +// v0.0.6 +// +// Copyright (c)2013 Derick Bailey, Muted Solutions, LLC. +// Distributed under MIT license +// +// http://github.com/babysitterjs/backbone.babysitter + +(function (root, factory) { + if (typeof exports === 'object') { + + var underscore = require('underscore'); + var backbone = require('backbone'); + + module.exports = factory(underscore, backbone); + + } else if (typeof define === 'function' && define.amd) { + + define(['underscore', 'backbone'], factory); + + } +}(this, function (_, Backbone) { + "option strict"; + + // Backbone.ChildViewContainer +// --------------------------- +// +// Provide a container to store, retrieve and +// shut down child views. + +Backbone.ChildViewContainer = (function(Backbone, _){ + + // Container Constructor + // --------------------- + + var Container = function(views){ + this._views = {}; + this._indexByModel = {}; + this._indexByCustom = {}; + this._updateLength(); + + _.each(views, this.add, this); + }; + + // Container Methods + // ----------------- + + _.extend(Container.prototype, { + + // Add a view to this container. Stores the view + // by `cid` and makes it searchable by the model + // cid (and model itself). 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; + + // index it by model + if (view.model){ + this._indexByModel[view.model.cid] = viewCid; + } + + // index by custom + if (customIndex){ + this._indexByCustom[customIndex] = viewCid; + } + + this._updateLength(); + }, + + // Find a view by the model that was attached to + // it. Uses the model's `cid` to find it. + findByModel: function(model){ + return this.findByModelCid(model.cid); + }, + + // Find a view by the `cid` of the model that was attached to + // it. Uses the model's `cid` to find the view `cid` and + // retrieve the view using it. + findByModelCid: function(modelCid){ + var viewCid = this._indexByModel[modelCid]; + 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]; + }, + + // 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; + + // delete model index + if (view.model){ + delete this._indexByModel[view.model.cid]; + } + + // delete custom index + _.any(this._indexByCustom, function(cid, key) { + if (cid === viewCid) { + delete this._indexByCustom[key]; + return true; + } + }, this); + + // 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){ + this.apply(method, _.tail(arguments)); + }, + + // 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){ + _.each(this._views, function(view){ + if (_.isFunction(view[method])){ + view[method].apply(view, args || []); + } + }); + }, + + // Update the `.length` attribute on this container + _updateLength: function(){ + this.length = _.size(this._views); + } + }); + + // 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']; + + _.each(methods, function(method) { + Container.prototype[method] = function() { + var views = _.values(this._views); + var args = [views].concat(_.toArray(arguments)); + return _[method].apply(_, args); + }; + }); + + // return the public API + return Container; +})(Backbone, _); + + return Backbone.ChildViewContainer; + +})); diff --git a/static/js/libs/backbone/backbone.marionette.js b/static/js/libs/backbone/backbone.marionette.js index 4380894..6ff513e 100644 --- a/static/js/libs/backbone/backbone.marionette.js +++ b/static/js/libs/backbone/backbone.marionette.js @@ -17,473 +17,24 @@ * https://github.com/marionettejs/backbone.wreqr/ */ -// Backbone.BabySitter -// ------------------- -// v0.0.5 -// -// Copyright (c)2013 Derick Bailey, Muted Solutions, LLC. -// Distributed under MIT license -// -// http://github.com/babysitterjs/backbone.babysitter +(function (root, factory) { + if (typeof exports === 'object') { -// Backbone.ChildViewContainer -// --------------------------- -// -// Provide a container to store, retrieve and -// shut down child views. + var underscore = require('underscore'); + var backbone = require('backbone'); + var wreqr = require('backbone.wreqr'); + var babysitter = require('backbone.babysitter'); -Backbone.ChildViewContainer = (function(Backbone, _){ - - // Container Constructor - // --------------------- + module.exports = factory(underscore, backbone, wreqr, babysitter); - var Container = function(initialViews){ - this._views = {}; - this._indexByModel = {}; - this._indexByCollection = {}; - this._indexByCustom = {}; - this._updateLength(); + } else if (typeof define === 'function' && define.amd) { - this._addInitialViews(initialViews); - }; + define(['underscore', 'backbone', 'backbone.wreqr', 'backbone.babysitter'], factory); - // Container Methods - // ----------------- + } +}(this, function (_, Backbone) { - _.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; - - // store the view - this._views[viewCid] = view; - - // 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 by custom - if (customIndex){ - this._indexByCustom[customIndex] = viewCid; - } - - 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 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 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]; - }, - - // Remove a view - remove: function(view){ - var viewCid = view.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 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 */function( window, document, undefined ) { + +(function( factory ) { + "use strict"; + + // Define as an AMD module if possible + if ( typeof define === 'function' && define.amd ) + { + define( ['jquery'], factory ); + } + /* Define using browser globals otherwise + * Prevent multiple instantiations if the script is loaded twice + */ + else if ( jQuery && !jQuery.fn.dataTable ) + { + factory( jQuery ); + } +} +(/** @lends */function( $ ) { + "use strict"; + /** + * DataTables is a plug-in for the jQuery Javascript library. It is a + * highly flexible tool, based upon the foundations of progressive + * enhancement, which will add advanced interaction controls to any + * HTML table. For a full list of features please refer to + * DataTables.net. + * + * Note that the DataTable object is not a global variable but is + * aliased to jQuery.fn.DataTable and jQuery.fn.dataTable through which + * it may be accessed. + * + * @class + * @param {object} [oInit={}] Configuration object for DataTables. Options + * are defined by {@link DataTable.defaults} + * @requires jQuery 1.3+ + * + * @example + * // Basic initialisation + * $(document).ready( function { + * $('#example').dataTable(); + * } ); + * + * @example + * // Initialisation with configuration options - in this case, disable + * // pagination and sorting. + * $(document).ready( function { + * $('#example').dataTable( { + * "bPaginate": false, + * "bSort": false + * } ); + * } ); + */ + var DataTable = function( oInit ) + { + + + /** + * Add a column to the list used for the table with default values + * @param {object} oSettings dataTables settings object + * @param {node} nTh The th element for this column + * @memberof DataTable#oApi + */ + function _fnAddColumn( oSettings, nTh ) + { + var oDefaults = DataTable.defaults.columns; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "sSortingClass": oSettings.oClasses.sSortable, + "sSortingClassJUI": oSettings.oClasses.sSortJUI, + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mData": oDefaults.mData ? oDefaults.oDefaults : iCol + } ); + oSettings.aoColumns.push( oCol ); + + /* Add a column specific filter */ + if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null ) + { + oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch ); + } + else + { + var oPre = oSettings.aoPreSearchCols[ iCol ]; + + /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */ + if ( oPre.bRegex === undefined ) + { + oPre.bRegex = true; + } + + if ( oPre.bSmart === undefined ) + { + oPre.bSmart = true; + } + + if ( oPre.bCaseInsensitive === undefined ) + { + oPre.bCaseInsensitive = true; + } + } + + /* Use the column options function to initialise classes etc */ + _fnColumnOptions( oSettings, iCol, null ); + } + + + /** + * Apply options for a column + * @param {object} oSettings dataTables settings object + * @param {int} iCol column index to consider + * @param {object} oOptions object with sType, bVisible and bSearchable etc + * @memberof DataTable#oApi + */ + function _fnColumnOptions( oSettings, iCol, oOptions ) + { + var oCol = oSettings.aoColumns[ iCol ]; + + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) + { + /* Backwards compatibility for mDataProp */ + if ( oOptions.mDataProp && !oOptions.mData ) + { + oOptions.mData = oOptions.mDataProp; + } + + if ( oOptions.sType !== undefined ) + { + oCol.sType = oOptions.sType; + oCol._bAutoType = false; + } + + $.extend( oCol, oOptions ); + _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); + + /* iDataSort to be applied (backwards compatibility), but aDataSort will take + * priority if defined + */ + if ( oOptions.iDataSort !== undefined ) + { + oCol.aDataSort = [ oOptions.iDataSort ]; + } + _fnMap( oCol, oOptions, "aDataSort" ); + } + + /* Cache the data get and set functions for speed */ + var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; + var mData = _fnGetObjectDataFn( oCol.mData ); + + oCol.fnGetData = function (oData, sSpecific) { + var innerData = mData( oData, sSpecific ); + + if ( oCol.mRender && (sSpecific && sSpecific !== '') ) + { + return mRender( innerData, sSpecific, oData ); + } + return innerData; + }; + oCol.fnSetData = _fnSetObjectDataFn( oCol.mData ); + + /* Feature sorting overrides column specific when off */ + if ( !oSettings.oFeatures.bSort ) + { + oCol.bSortable = false; + } + + /* Check that the class assignment is correct for sorting */ + if ( !oCol.bSortable || + ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; + } + else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortable; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI; + } + else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableAsc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; + } + else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableDesc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; + } + } + + + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( oSettings ) + { + /* Not interested in doing column width calculation if auto-width is disabled */ + if ( oSettings.oFeatures.bAutoWidth === false ) + { + return false; + } + + _fnCalculateColumnWidths( oSettings ); + for ( var i=0 , iLen=oSettings.aoColumns.length ; i