for scrolling purposes\n * originally using css calc() but in conjuction with a transition it\n * crashes safari.\n * http://stackoverflow.com/questions/14055461/ios-safari-css-calc-css-transition-instant-crash\n */\n self.on(\"calc\", function () {\n //var h = window.innerHeight - self.$shelfDrawerNavInternal.height();\n //self.$shelfDrawerWrap.height(h);\n })\n\n /**\n * Bind any user interaction events here\n * @public\n */\n self.bindEvents = function () {\n self.$navLinks\n .add(self.$navMobileLinks)\n .off(\"click\")\n .on(\"click\", function (e) {\n e.preventDefault()\n var targ = $(this).attr(\"href\").replace(/^#/, \"\")\n if (targ.includes(\"/users/\")) {\n return mm.router.navigate(`/users/${mm.user.attributes.id}`, { trigger: true })\n }\n if (targ === \"favorites\") {\n return mm.router.navigate(\"/favorites\", { trigger: true })\n }\n // Hacky solution: once page is routed to /manage force a reload since\n // Backbone won't allow routing to active admin directly\n if (targ === \"manage\") {\n window.location.href = \"/manage\"\n return true\n }\n var state = targ === \"project-searches\" ? \"projectSearches\" : targ\n\n self._current(targ)\n self.setState(state)\n self.delay = false\n })\n\n self.$shelfClose.on(\"click\", function (e) {\n e.preventDefault()\n self.delay = true\n self.setState(self.userOrGuest)\n })\n\n self.$menuAnchor.off(\"click\").on(\"click\", function (e) {\n e.preventDefault()\n if (self.$desktopNav.hasClass(\"pushed\")) {\n self.close()\n } else {\n self.setState(\"nav\")\n }\n })\n\n self.$playerAnchor.on(\"click\", function (e) {\n e.preventDefault()\n self.setState(\"nav\")\n })\n\n self.$shelfNavAnchor.on(\"click\", function (e) {\n e.preventDefault()\n self.setState(\"nav\")\n })\n\n self.$shelfNavAnchorLinks.on(\"click\", function (e) {\n if ($(this).attr(\"data-external\")) {\n return true\n }\n e.preventDefault()\n self.close()\n })\n }\n\n /**\n * Exposes close functionality to drawerPages, checks guest status and sets\n * state accordingly.\n * @public\n * @param {Function} callback Usually a drawerPage.hide method\n */\n self.close = function (callback) {\n self.userOrGuest = mm.user.isLoggedIn() ? \"user\" : \"guest\"\n self.delay = true\n self.setState(self.userOrGuest)\n if (typeof callback === \"function\") {\n self._checkDelay(callback)\n }\n }\n\n /**\n * Checks to see if the drawer should delay hiding a drawerPage until after\n * the drawer has closed; to be used internally.\n * @public\n * @param {Function} callback Function to call, either delayed or not\n * @return {Boolean}\n */\n self._checkDelay = function (callback) {\n if (typeof callback !== \"function\") return false\n if (self.delay && typeof callback === \"function\") {\n self._callbacks.push(callback)\n // setTimeout(callback, 500);\n return true\n }\n callback()\n return false\n }\n\n /**\n * Applies a current state to the desktop drawer navigation; desktop only\n * @public\n * @param {String} targ The link to be made current\n */\n self._current = function (targ) {\n self.$navLinks\n .removeClass(\"over\")\n .filter('[href=\"#' + targ + '\"]')\n .addClass(\"over\")\n }\n\n /**\n * Removes classes to 'pull' the drawer closed; to be used internally\n * @public\n * @return {Boolean}\n */\n self._pull = function () {\n if (self.pushed) {\n self.$desktopNav.removeClass(\"pushed\")\n self.$el.removeClass(\"viewing\")\n self.$shelf.removeClass(\"onscreen\")\n self.$navLinks.removeClass(\"over\")\n self.pushed = false\n $(\"#container\").off(\"click\")\n self.$container[0].addEventListener(\"transitionend\", function tranEnd() {\n var call\n while ((call = self._callbacks.shift())) call()\n self.$container[0].removeEventListener(\"transitionend\", tranEnd)\n })\n return true\n }\n return false\n }\n\n /**\n * Adds classes to 'push' the drawer open; to be used internally.\n * @public\n * @return {Boolean}\n */\n self._push = function () {\n self.pushing = true\n\n var pushed = false\n\n if (mm.isMobile) {\n self.trigger(\"calc\")\n }\n\n if (!self.pushed) {\n self.$desktopNav.addClass(\"pushed\")\n self.$el.addClass(\"viewing\")\n self.$shelf.addClass(\"onscreen\")\n self.pushed = true\n\n // on page click, close drawer\n _.defer(function () {\n $(\"#container\").on(\"click\", function () {\n if (!self.pushing) {\n mm.drawer.trigger(\"close\")\n }\n })\n })\n\n pushed = true\n }\n\n _.defer(function () {\n self.pushing = false\n self.delay = false\n })\n\n return pushed\n }\n\n self._render = function () {\n $.get(\"/render_drawer\").done(function (data) {\n self.$target.html(data)\n self.init()\n })\n }\n\n self.spin = function (spin) {\n if (spin) {\n self.spinner.spin(self.$shelfClose[0])\n self.$shelfClose.addClass(\"spinning\")\n } else {\n self.spinner.stop()\n self.$shelfClose.removeClass(\"spinning\")\n }\n }\n\n self._onOrderLoginPage = function () {\n return /click_order\\/[0-9]*\\/login/.test(Backbone.history.fragment)\n }\n\n self._attachOrderToLoginForm = function () {\n const orderId = window.location.pathname.match(/\\d+/)[0]\n $(\"#orderId\").val(orderId)\n }\n\n /**\n * State Machine map, do not overwrite, only extend.\n * @public\n * @type {Object}\n */\n self.states = {\n onGuest: function () {\n self.$navGuestLinks.add(self.$navMobileGuestLinks).removeClass(\"hidden\")\n self.$navUserLinks.add(self.$navMobileUserLinks).addClass(\"hidden\")\n self.$navInternalUserLinks.addClass(\"hidden\")\n self._pull()\n },\n onUser: function () {\n self.$navGuestLinks.add(self.$navMobileGuestLinks).addClass(\"hidden\")\n self.$navUserLinks.add(self.$navMobileUserLinks).removeClass(\"hidden\")\n if (mm.user.attributes.internal_user) {\n self.$navInternalUserLinks.removeClass(\"hidden\")\n }\n self._pull()\n },\n\n onCart: function () {\n mm.drawerCartPage.show()\n self._push()\n },\n offCart: function () {\n self._checkDelay(mm.drawerCartPage.hide)\n },\n\n onCartbench: function (payload, remove) {\n mm.drawerCartbenchPage.show(payload, remove)\n self._push()\n },\n offCartbench: function () {\n self._checkDelay(mm.drawerCartbenchPage.hide)\n },\n\n onCustomize: function (payload) {\n mm.drawerCustomizePage.show(payload)\n self._push()\n },\n offCustomize: function () {\n self._checkDelay(mm.drawerCustomizePage.hide)\n },\n\n onLogin: function () {\n var $link = $(\".dashboard-link\")\n var $searchLink = $(\".project-searches\")\n mm.drawerLoginPage.show()\n self._push()\n if (self._onOrderLoginPage()) self._attachOrderToLoginForm()\n\n mm.user.once(\"logged_in\", function () {\n if (self._onOrderLoginPage()) {\n self.close(function () {})\n } else if (mm.isMobile) {\n self.close()\n window.location.reload()\n } else {\n self.close(function () {\n mm.facade.trigger(\"refresh\")\n })\n }\n\n const userType = mm.user.attributes.role\n const isInternalUser = mm.user.attributes.internal_user\n if (!$link.hasClass(\"available\")) {\n if (isInternalUser || userType === \"international_partner\") $link.text(\"Portal\")\n if (userType === \"vendor\") $link.text(\"Artist Portal\")\n $link.addClass(\"available\")\n $(\"nav[role=main].desktop ol li\").addClass(\"condensed\")\n }\n if (!isInternalUser) {\n $searchLink.addClass(\"hidden\")\n }\n })\n },\n offLogin: function () {\n self._checkDelay(mm.drawerLoginPage.hide)\n },\n\n onLogout: function () {\n var $link = $(\".dashboard-link\")\n mm.user.logout(self.close)\n mm.user.once(\"logged_out\", function () {\n if (/users\\/[0-9]*/.test(Backbone.history.fragment)) {\n mm.router.navigate(\"/\", { trigger: true, replace: true })\n } else if (/order\\/[0-9]*/.test(Backbone.history.fragment)) {\n mm.router.navigate(\"/\", { trigger: true, replace: true })\n } else {\n window.location.reload()\n }\n if ($link.hasClass(\"available\")) {\n $link.removeClass(\"available\")\n $(\"nav[role=main].desktop ol li\").removeClass(\"consdensed\")\n }\n })\n },\n\n onMixbench: function (payload) {\n mm.drawerMixbenchPage.show(payload)\n self._push()\n },\n offMixbench: function () {\n self._checkDelay(mm.drawerMixbenchPage.hide)\n },\n\n onMixtapes: function () {\n mm.drawerMixtapesPage.show()\n self._push()\n },\n offMixtapes: function () {\n self._checkDelay(mm.drawerMixtapesPage.hide)\n },\n\n onNav: function () {\n mm.drawerNavPage.show()\n self._push()\n },\n offNav: function () {\n self._checkDelay(mm.drawerNavPage.hide)\n },\n\n onProjectSearchbench: function (payload) {\n mm.drawerProjectSearchbenchPage.show(payload)\n self._push()\n },\n offProjectSearchbench: function () {\n self._checkDelay(mm.drawerProjectSearchbenchPage.hide)\n },\n\n onProjectSearches: function () {\n mm.drawerProjectSearchesPage.show()\n self._push()\n },\n offProjectSearches: function () {\n self._checkDelay(mm.drawerProjectSearchesPage.hide)\n },\n\n onRegister: function () {\n mm.drawerRegisterPage.show()\n self._push()\n },\n offRegister: function () {\n self._checkDelay(mm.drawerRegisterPage.hide)\n },\n\n onShare: function (payload) {\n mm.drawerSharePage.show(payload)\n self._push()\n },\n offShare: function () {\n self._checkDelay(mm.drawerSharePage.hide)\n },\n\n onActivation: function () {\n mm.drawerActivationPage.show()\n self._push()\n },\n\n offActivation: function () {\n self._checkDelay(mm.drawerActivationPage.hide)\n },\n\n onSubscribe: function () {\n mm.drawerNewsletterPage.show()\n self._push()\n },\n offSubscribe: function () {\n self._checkDelay(mm.drawerNewsletterPage.hide)\n },\n }\n\n mm.facade.on(\"refresh\", self._render)\n mm.facade.on(\"desktop mobile\", self.close)\n\n self.on(\"cart\", function () {\n self.setState(\"cart\")\n })\n\n self.on(\"cart:add\", function (payload, remove) {\n remove = typeof remove === \"undefined\" ? false : remove\n self.setState(\"cartbench\", payload, remove)\n })\n\n self.on(\"checkNav\", function (payload) {\n self._current(payload)\n })\n\n self.on(\"close\", function (callback) {\n callback = callback || undefined\n self.close(callback)\n })\n\n self.on(\"customize\", function (payload) {\n self.setState(\"customize\", payload)\n })\n\n self.on(\"mixtapes\", function () {\n self.setState(\"mixtapes\")\n })\n\n self.on(\"mixtape:add\", function (payload) {\n if (!mm.user.isLoggedIn()) return false\n self.setState(\"mixbench\", payload)\n })\n\n self.on(\"projectSearches\", function () {\n self.setState(\"projectSearches\")\n })\n\n self.on(\"projectSearch:add\", function (payload) {\n if (!mm.user.isLoggedIn()) return false\n self.setState(\"projectSearchbench\", payload)\n })\n\n self.on(\"register\", function () {\n self.setState(\"register\")\n })\n\n self.on(\"activation\", function () {\n self.setState(\"activation\")\n })\n\n self.on(\"login\", function () {\n self.setState(\"login\")\n })\n\n self.on(\"subscribe\", function () {\n self.setState(\"subscribe\")\n })\n\n self.on(\"share:mixtape\", function (payload) {\n self.setState(\"share\")\n mm.drawerSharePage.trigger(\"shareData\", payload)\n })\n\n self.on(\"share:songVersion\", function (payload) {\n self.setState(\"share\")\n mm.drawerSharePage.trigger(\"shareData\", payload)\n })\n\n return self\n})()\n\nmm.facade.on(\"app:ready\", mm.drawer.init)\n","mm.DrawerActivationPage = function () {\n var self = mm.DrawerPage($(\".mm-drawer-wrap #activation\")),\n form = mm.DrawerForm($(\".mm-drawer-form\", self.$el), {\n url: \"/account/activation\",\n type: \"GET\",\n empty: true,\n })\n\n var superHide = self.hide\n self.hide = function () {\n superHide()\n }\n\n var superShow = self.show\n self.show = function () {\n superShow()\n }\n\n return self\n}\n","/**\n * Scripts specific to the drawer cart page\n * @public\n * @return {Object} Instance of mm.DrawerCartPage\n */\nmm.DrawerCartPage = function () {\n var self = mm.DrawerPage($(\".mm-drawer-wrap #cart\")),\n $checkout = $(\"a.checkout\", self.$el),\n $explore = $(\"a.explore\", self.$el),\n $count = $(\".count\", self.$el),\n $subtotal = $(\".totals .subtotal\", self.$el),\n $targ = $(\".mm-cart-target\", self.$el),\n $total = $(\".totals .total\", self.$el),\n $removes,\n $replacements,\n _pendingRemovalID\n\n var _linetemplate =\n '<%= label %>' + '<%= value %>'\n\n var _template =\n \"<% _.each(items, function (item) { %>\" +\n \"\" +\n \"<% if (item.song_version) { %>\" +\n '' +\n '<%= item.song_version.title %>' +\n '<%= mm.format.numberToCurrency(item.price) %>' +\n \"
\" +\n '' +\n '<%= item.song_version.artist.name %>' +\n '<%= item.license_version.name %>' +\n \"
\" +\n \"<% } else { %>\" +\n '' +\n '<%= item.line_item_type %>' +\n '<%= mm.format.numberToCurrency(item.price) %>' +\n \"
\" +\n \"<% } %>\" +\n '\">x' +\n \"\" +\n \"<% }); %>\"\n\n function parseTemplate() {\n var template = _.template(_template)\n var line_items = { items: mm.cart.order.line_items }\n var lineTemplate = _.template(_linetemplate)\n var sub = {\n label: \"Subtotal\",\n value: mm.format.numberToCurrency(mm.cart.order.price),\n }\n var total = {\n label: \"Total\",\n value: mm.format.numberToCurrency(mm.cart.order.total),\n }\n\n if (mm.cart.order.line_items.length > 0) {\n $targ.html(template(line_items))\n $subtotal.html(lineTemplate(sub))\n $total.html(lineTemplate(total))\n $count.html(pluralize(mm.cart.order.line_items.length, \"Item\"))\n const loginPath = mm.user.isLoggedIn() ? \"\" : \"/login\"\n $checkout.attr(\"href\", \"/click_order/\" + mm.cart.order.id + loginPath)\n $checkout.show()\n $subtotal.show()\n $total.show()\n $count.show()\n } else {\n $targ.html(\"Your cart is empty!
\")\n $subtotal.hide()\n $total.hide()\n $count.hide()\n $checkout.hide()\n }\n\n self.rebuild()\n mm.drawer.spin(false)\n }\n\n function pluralize(count, str) {\n if (count > 1 || count === 0) {\n return count + \" \" + str + \"s\"\n }\n return count + \" \" + str\n }\n\n jQuery.easing[\"easeOutQuad\"] = function (x, t, b, c, d) {\n return -c * (t /= d) * (t - 2) + b\n }\n\n function removeLine(id, that) {\n var _data = {\n id: mm.cart.order.id,\n line_item: id,\n }\n mm.drawer.spin(true)\n $.post(\"/remove-line\", _data)\n .done(function (data) {\n $(that)\n .parent(\"li\")\n .animate(\n {\n opacity: 0,\n height: 0,\n paddingTop: 0,\n paddingBottom: 0,\n },\n {\n duration: 400,\n easing: \"easeOutQuad\",\n complete: function () {\n $(this).remove()\n mm.cart.hydrate(parseTemplate)\n },\n }\n )\n if (data.line_item_count < 1) {\n $(\"#mm-drawer-nav li.cart\").addClass(\"hidden\")\n }\n })\n .fail(function (data) {\n console.log(\"fail\")\n })\n }\n\n var superRebuild = self.rebuild\n self.rebuild = function () {\n superRebuild()\n $removes = $(\"[data-action=remove]\", self.$el)\n $replacements = $(\"[data-action=replace]\", self.$el)\n $songTitles = $(\"span.title\", self.$el)\n\n $removes.on(\"click\", function (e) {\n e.preventDefault()\n e.stopImmediatePropagation()\n var id = $(this).attr(\"data-id\")\n removeLine(id, this)\n })\n\n $replacements.on(\"click\", function (e) {\n e.preventDefault()\n e.stopImmediatePropagation()\n _pendingRemovalID = $(this).attr(\"data-id\")\n mm.drawer.trigger(\"cart:add\", $(this).attr(\"data-song-version-id\"), true)\n })\n\n var hasLongTitle = function (displayWidth) {\n return (mm.isMobile && displayWidth > 124) || displayWidth === 170\n }\n\n $songTitles.hover(\n function (e) {\n var title = $(e.target)\n var displayWidth = title.width()\n\n if (hasLongTitle(displayWidth)) {\n var actualWidth = title.prop(\"scrollWidth\")\n var transitionWidth = actualWidth - displayWidth\n\n title.css({\n transition: transitionWidth / 100 + \"s linear\",\n overflow: \"visible\",\n transform: \"translateX(-\" + transitionWidth + \"px)\",\n })\n }\n },\n function (e) {\n var title = $(e.target)\n var displayWidth = title.width()\n\n if (hasLongTitle(displayWidth)) {\n var actualWidth = title.prop(\"scrollWidth\")\n var transitionWidth = actualWidth - displayWidth\n var transitionDuration = transitionWidth > 200 ? 0.8 : transitionWidth / 100\n\n title.css({\n transition: transitionDuration + \"s linear\",\n overflow: \"hidden\",\n transform: \"none\",\n })\n }\n }\n )\n\n $checkout.on(\"click\", function () {\n fbq(\"track\", \"InitiateCheckout\")\n mm.drawerProxy.trigger(\"close\")\n })\n\n $explore.on(\"click\", function (e) {\n e.preventDefault()\n if (window.location.pathname === \"/browse\") {\n mm.drawerProxy.trigger(\"close\")\n } else {\n $explore.attr({ href: \"/browse\", \"data-bypass\": \"true\" })\n var href = $(this).attr(\"href\")\n mm.router.navigate(href, { trigger: true })\n }\n })\n }\n\n /**\n * Extends base self.show method, sets template compilation in motion.\n * @public\n * @return {Object} mm.drawerCartPage\n */\n var superShow = self.show\n self.show = function () {\n parseTemplate()\n superShow()\n return self\n }\n\n self.remove = function (id, callback) {\n $.post(\"/remove-line\", { id: mm.cart.order.id, line_item: id }).done(callback)\n }\n\n if (mm.drawer) {\n mm.drawer.on(\"cart:remove\", function (callback) {\n if (typeof callback !== \"function\") {\n callback = function () {}\n }\n self.remove(_pendingRemovalID, callback)\n })\n }\n\n return self\n}\n","/**\n * The drawer page containing any functionality related to adding a license to\n * the cart object. This differs from mm.DrawerCartPage.\n * @public\n * @extends {mm.DrawerPage}\n * @return {Object} mm.DrawerCartbenchPage\n */\nmm.DrawerCartbenchPage = function () {\n \"use strict\"\n\n var self = mm.DrawerPage($(\".mm-drawer-wrap #cartbench\")),\n form,\n $licenses,\n $cartbench = $(\".mm-drawer-wrap #cartbench\"),\n $license_versions,\n _licenseID,\n _licenseIndex,\n _pendingSongVersionID,\n _remove = false,\n songVersionExStatus,\n _pendingSongVersion\n\n /**\n * Fetch the parsed template from the server showing the proper grouping of\n * licenses and options.\n * @private\n */\n function fetchTemplate(callback) {\n mm.drawer.spin(true)\n $.get(\"/song_versions/licenses/\" + _pendingSongVersionID).done(function (data) {\n self.$el.html(data)\n self.rebuild()\n mm.drawer.spin(false)\n if (typeof callback === \"function\") callback()\n })\n }\n\n /**\n * With both the license_id and song_version_id set, submit a POST request to the\n * server and hydrate the mm.cart object with the response.\n * @private\n */\n function processLineItem() {\n var item = {\n line_item: {\n song_version_id: _pendingSongVersionID,\n license_version_id: _licenseID,\n },\n }\n mm.drawer.spin(true)\n if (_remove) {\n mm.drawer.trigger(\"cart:remove\", function () {\n process(item)\n })\n } else {\n process(item)\n }\n }\n\n function process(item) {\n $.post(\"/add-to-cart\", item)\n .done(function (data) {\n if (data.success) $(\"#mm-drawer-nav li.cart\").removeClass(\"hidden\")\n mm.user.cart.hydrate(function () {\n mm.drawer.spin(false)\n mm.drawer.delay = false\n mm.drawerProxy.trigger(\"cart\")\n })\n })\n .fail(function (data) {\n console.error(data)\n })\n }\n\n /**\n * Extends base self.hide method to reset our song version and license id's\n * @public\n * @return {Object} mm.drawerCartbenchPage\n */\n var superHide = self.hide\n self.hide = function () {\n _pendingSongVersion = null\n _pendingSongVersionID = null\n _licenseID = null\n _remove = false\n superHide()\n if (!_.isUndefined(form)) {\n form.reset()\n }\n return self\n }\n\n /**\n * Extends base self.rebuild method, sets and rebinds drawer specific\n * elements.\n * @public\n * @return {Object} mm.drawerCartbenchPage\n */\n var superRebuild = self.rebuild\n self.rebuild = function () {\n superRebuild()\n $licenses = $(\"[data-license-targ]\", self.$el)\n $license_versions = $(\".license\", $licenses)\n\n if (_pendingSongVersion) songVersionExStatus = _pendingSongVersion.exclusive\n\n var localNestLinks = $(\".local-nest\", self.$el)\n\n self.$nestLinks.on(\"click\", function (e) {\n e.preventDefault()\n var targ\n targ = $(this).attr(\"data-license\")\n var license = $('[data-license-targ=\"' + targ + '\"]', self.$el)\n $licenses.not(license).removeClass(\"active\")\n license.addClass(\"active\")\n })\n\n // hack to route custom license button without page reload\n // TODO: look into proper way to do later\n $(\"#custom-license-req\").on(\"click\", function (e) {\n e.preventDefault()\n var href = $(this).attr(\"href\")\n mm.router.navigate(href, { trigger: true })\n })\n\n localNestLinks.on(\"click\", function (e) {\n e.preventDefault()\n var targ = $(this).attr(\"href\").replace(/^#/, \"\")\n mm.drawer.delay = false\n mm.drawer.setState(targ)\n })\n\n $license_versions.on(\"click\", function (e) {\n e.preventDefault()\n var license_name = $(this).data(\"license-name\")\n fbq(\"track\", \"AddToCart\")\n _licenseID = $(this).attr(\"data-license-id\")\n _licenseIndex = $(this).attr(\"data-index\")\n $license_versions.off(\"click\")\n\n if (songVersionExStatus === true) {\n exLicenseTarg()\n } else if (license_name === \"Podcast License\") {\n renderPodcastOption()\n } else {\n processLineItem()\n }\n })\n\n return self\n }\n\n function renderPodcastOption() {\n var targ = \"podcast\"\n\n var license = $('[data-license-targ=\"' + targ + '\"]', self.$el)\n $licenses.not(license).removeClass(\"active\")\n license.addClass(\"active\")\n $(\"#podcast-opt-\" + _licenseIndex).fadeIn(\"fast\")\n\n // if back button used for selected podcast option\n $(\"#mm-drawer .back\").on(\"click\", function (e) {\n e.preventDefault()\n if ($('[data-license-targ=\"podcast\"]').hasClass(\"active\")) {\n $('[data-license-targ=\"podcast\"]').removeClass(\"active\")\n $(\"#podcast-opt-\" + _licenseIndex).hide()\n _licenseID = null\n self.rebuild()\n }\n })\n\n // add podcast license to cart\n var button = $(\".podcast-submit\")\n\n button.on(\"click\", function (e) {\n e.preventDefault()\n processLineItem()\n })\n }\n\n // checks to see if song version is exclusive and changes target to show exclusive deets\n // instead of rendering cart items\n function exLicenseTarg() {\n var targ = \"exclusive\"\n\n var license = $('[data-license-targ=\"' + targ + '\"]', self.$el)\n $licenses.not(license).removeClass(\"active\")\n license.addClass(\"active\")\n }\n\n // user agrees that song version is not exclusive by checking box\n $(\".mm-drawer-wrap\").on(\"change\", \"#excheck\", function (e) {\n exEnableContinue()\n })\n\n function exEnableContinue() {\n var button = $(\".ex-button\")\n\n if ($(\"#excheck\").prop(\"checked\") === true) {\n button.removeAttr(\"disabled\")\n button.addClass(\"available\")\n } else {\n button.removeClass(\"available\")\n button.attr(\"disabled\", true)\n }\n\n button.off(\"click.ex\").on(\"click.ex\", function (e) {\n e.preventDefault()\n processLineItem()\n })\n }\n\n /**\n * Extends base self.show method, checks for payload (song_version_id) and fetches\n * the template if it exists.\n * @public\n * @return {Object} mm.drawerCartbenchPage\n */\n var superShow = self.show\n self.show = function (payload, remove) {\n _remove = remove\n payload = typeof payload === \"string\" ? { id: parseInt(payload) } : payload\n\n if (payload && _.has(payload, \"id\")) {\n _pendingSongVersion = payload\n _pendingSongVersionID = payload.id\n fetchTemplate(function () {\n if (!payload.has_click_licenses) {\n $cartbench.addClass(\"left\")\n }\n })\n }\n superShow()\n return self\n }\n\n return self\n}\n","mm.DrawerCustomizePage = function () {\n \"use strict\"\n\n var self = mm.DrawerPage($(\".mm-drawer-wrap #customize\")),\n form = mm.DrawerForm($(\".mm-drawer-form\", self.$el), {\n type: \"POST\",\n empty: true,\n }),\n _songVersionID\n\n var superHide = self.hide\n self.hide = function () {\n superHide()\n form.reset()\n }\n\n var superShow = self.show\n self.show = function (songVersionID) {\n superShow()\n _songVersionID = songVersionID\n form.reset()\n form.options.url = \"/song_versions/\" + _songVersionID + \"/customize\"\n form.off(\"submit\").on(\"submit\", form.submit)\n self.resize()\n }\n\n return self\n}\n","/**\n * Scripts specific to the drawer login page\n * @public\n * @return {Object} Instance of mm.DrawerLoginPage\n */\nmm.DrawerLoginPage = function () {\n var self = mm.DrawerPage($(\".mm-drawer-wrap #login\")),\n forgotForm = mm.DrawerForm($(\".mm-drawer-form.forgot-form\", self.$el), {\n url: \"/account/reset_password\",\n type: \"POST\",\n empty: false,\n }),\n loginForm = mm.DrawerForm($(\".mm-drawer-form.login\", self.$el), {}),\n $register = $(\".register\", self.$el),\n $activation = $(\".activation\", self.$el)\n\n self.$message = $(\".message\", self.$el)\n\n $register.on(\"click\", function (e) {\n e.preventDefault()\n mm.drawerProxy.trigger(\"checkNav\", \"register\")\n mm.drawerProxy.trigger(\"register\")\n })\n\n $activation.on(\"click\", function (e) {\n e.preventDefault()\n mm.drawerProxy.trigger(\"checkNav\", \"activation\")\n mm.drawerProxy.trigger(\"activation\")\n })\n\n /*!\n * Begin form method overides\n */\n\n loginForm.handleErrors = function (response) {\n loginForm.$errors.addClass(\"display\")\n return loginForm\n }\n\n loginForm.handleSuccess = function (response) {\n loginForm.$errors.removeClass(\"display\")\n loginForm.$success.addClass(\"display\")\n loginForm.$hides.addClass(\"hide\")\n loginForm.$form.addClass(\"success\")\n setTimeout(function () {\n mm.drawerProxy.trigger(\"close\")\n }, 1000)\n\n return loginForm\n }\n\n loginForm.reset = function () {\n if (mm.isMobile) {\n loginForm.$inputs.removeClass(\"invalid\")\n } else {\n loginForm.$inputs.removeClass(\"invalid\").first().focus()\n }\n loginForm.$errors.removeClass(\"display\")\n loginForm.$success.removeClass(\"display\")\n loginForm.$hides.removeClass(\"hide\")\n loginForm.$form.removeClass(\"success\")\n loginForm.$form[0].reset()\n return loginForm\n }\n\n loginForm.submit = function (e) {\n e.preventDefault()\n loginForm.spin(true)\n var email = loginForm.$inputs.filter('[name=\"email\"]').val()\n var pass = loginForm.$inputs.filter('[name=\"password\"]').val()\n var order_id = loginForm.$inputs.filter('[name=\"order_id\"]').val()\n\n mm.user\n .login(email, pass, order_id)\n .done(loginForm.handleSuccess)\n .fail(loginForm.handleFail)\n .always(loginForm.complete)\n\n return loginForm\n }\n\n // bind submit _after_ redefining submit handler\n loginForm.$form.off(\"submit\").on(\"submit\", loginForm.submit)\n\n var superHide = self.hide\n self.hide = function () {\n superHide()\n loginForm.reset()\n forgotForm.reset()\n self.notifyReset()\n }\n\n var superShow = self.show\n self.show = function () {\n superShow()\n loginForm.reset()\n forgotForm.reset()\n }\n\n /**\n * display a message in the login form\n * @param {[type]} msg [description]\n * @return {[type]} [description]\n */\n self.notify = function (msg) {\n self.$message.html(msg || \"\").addClass(\"display\")\n }\n\n self.notifyReset = function () {\n self.$message.empty().removeClass(\"display\")\n }\n\n return self\n}\n","mm.DrawerMixbenchPage = function () {\n \"use strict\"\n\n var self = mm.DrawerPage($(\".mm-drawer-wrap #mixbench\")),\n $form = $(\"form\", self.$el),\n $mixtapes,\n $name = $(\"input[type=text]\", $form),\n $description = $('textarea[name=\"description\"]', $form),\n $inputs = $(\"input\", $form),\n $privacy = $(\"input#privacy\", $form),\n $submit = $(\"button[type=submit]\", $form),\n _spinTarg = $(\"button\", $form).parent(\"div\")[0],\n _spinner = new mm.Spinner(),\n _songVersionID\n\n $form.on(\"submit\", onSubmit)\n $submit.attr(\"disabled\", true)\n\n function alwaysRun() {\n $privacy.prop(\"checked\", false)\n $(\"label .icon.secret\", $form).addClass(\"hidden\")\n $name.blur().val(\"\")\n }\n\n function build() {\n $mixtapes = $(\"li[data-mixtape-id]\", self.$el)\n $privacy.prop(\"checked\", false)\n $(\"label .icon.secret\", $form).addClass(\"hidden\")\n\n $privacy.off(\"change\").on(\"change\", function (e) {\n var choice = $(this).is(\":checked\") ? \"public\" : \"secret\"\n $(\"> label\", $form).removeClass(\"hidden\")\n $(\"> label.\" + choice, $form).addClass(\"hidden\")\n\n $(\"label .icon\", $form).removeClass(\"hidden\")\n $(\"label .icon.\" + choice, $form).addClass(\"hidden\")\n })\n\n $mixtapes.on(\"click\", function (e) {\n e.preventDefault()\n var mixtapeID = $(this).attr(\"data-mixtape-id\")\n _spinner.spin(_spinTarg)\n $inputs.attr(\"disabled\", true)\n $form.addClass(\"processing\")\n $.ajax({\n url: \"/mixtapes/\" + mixtapeID + \"/add/\" + _songVersionID,\n success: onSuccess,\n })\n })\n\n $name.on(\"keyup keydown\", function (e) {\n if (this.value !== \"\") {\n $submit.removeAttr(\"disabled\")\n } else {\n $submit.attr(\"disabled\", true)\n }\n })\n\n return self\n }\n\n function onFailure(e) {\n console.error(e)\n }\n\n function onSubmit(e) {\n e.preventDefault()\n if ($name.val() === \"\") return false\n var mixtape,\n data = {\n name: $name.val(),\n description: $description.val(),\n secret: $privacy.is(\":checked\"),\n }\n if (_songVersionID) {\n data.song_version = _songVersionID\n }\n $name.off(\"keyup keydown\")\n mixtape = mm.Mixtape(data)\n\n _spinner.spin(_spinTarg)\n $inputs.attr(\"disabled\", true)\n $form.addClass(\"processing\")\n\n mm.user.mixtapes.add(mixtape)\n mixtape.create().done(onSuccess).fail(onFailure).always(alwaysRun)\n }\n\n function onSuccess(data) {\n _spinner.stop()\n $inputs.removeAttr(\"disabled\")\n $form.removeClass(\"processing\")\n _songVersionID = undefined\n $mixtapes.off(\"click\")\n mm.drawer.delay = false\n mm.drawerProxy.trigger(\"mixtapes\")\n }\n\n function rerender() {\n $.ajax({\n method: \"POST\",\n url: \"/mixtapes/user\",\n data: { style: \"benchlist\" },\n success: function (data) {\n $(\".list\", self.$el).html(data)\n build()\n },\n })\n }\n\n var superShow = self.show\n self.show = function (payload) {\n rerender()\n superShow()\n _songVersionID = payload\n }\n\n return build()\n}\n","mm.DrawerNavPage = function () {\n \"use strict\"\n\n var self = mm.DrawerPage($(\".mm-drawer-wrap #nav\"))\n\n self.showNested = function () {}\n\n return self\n}\n","mm.DrawerNewsletterPage = function () {\n var self, form\n\n self = mm.DrawerPage($(\".mm-drawer-wrap #subscribe\"))\n form = mm.DrawerForm($(\".mm-drawer-form\", self.$el), {\n url: \"/newsletter\",\n type: \"POST\",\n })\n\n return self\n}\n","mm.DrawerProjectSearchbenchPage = function () {\n \"use strict\"\n\n var self = mm.DrawerPage($(\".mm-drawer-wrap #project-searchbench\")),\n $form = $(\"form\", self.$el),\n $addToSearchOrDirection,\n $searchItemWithDirections,\n $name = $(\"input[type=text]\", $form),\n $description = $('textarea[name=\"description\"]', $form),\n $inputs = $(\"input\", $form),\n $submit = $(\"button[type=submit]\", $form),\n $viewMoreSearches,\n _limit = 10,\n _spinTarg = $(\"button\", $form).parent(\"div\")[0],\n _spinner = new mm.Spinner(),\n _songVersionId,\n _songId\n\n $form.on(\"submit\", onSubmit)\n $submit.attr(\"disabled\", true)\n\n function alwaysRun() {\n $name.blur().val(\"\")\n $description.blur().val(\"\")\n }\n\n function build() {\n $addToSearchOrDirection = $(\".add-to\", self.$el)\n $searchItemWithDirections = $(\".with-directions\", self.$el)\n $viewMoreSearches = $(\"button.view-more-searches\", self.$el)\n\n $addToSearchOrDirection.on(\"click\", function (e) {\n e.preventDefault()\n e.stopPropagation()\n var $projectSearchContainer = $(this).closest(\".project-search-item-container\")\n var $projectSearchItem = $projectSearchContainer.find(\"li.project-search-item\")\n var directionId = parseInt($(this).data(\"direction-id\"))\n var projectSearchId = parseInt($projectSearchContainer.data(\"project-search-id\"))\n var comparativeIds = $projectSearchContainer.data(\"project-search-comparative-ids\")\n\n if (isSongVersionPresent($projectSearchItem, comparativeIds)) return false\n _spinner.spin(_spinTarg)\n $inputs.attr(\"disabled\", true)\n $form.addClass(\"processing\")\n $.ajax({\n type: \"POST\",\n url: \"/api/v1/search_rounds/add/\" + _songVersionId,\n data: {\n search_round: {\n project_search_id: projectSearchId,\n direction_id: directionId,\n },\n },\n success: function (data) {\n data.success ? onSuccess(data) : onError($projectSearchItem, data.message)\n },\n })\n })\n\n $name.on(\"keyup keydown input propertychange\", function (e) {\n if (this.value !== \"\") {\n $submit.removeAttr(\"disabled\")\n } else {\n $submit.attr(\"disabled\", true)\n }\n })\n\n $searchItemWithDirections.on(\"click\", function (e) {\n e.preventDefault()\n\n if ($(this).hasClass(\"expanded\")) {\n $(this).removeClass(\"expanded\")\n } else {\n $searchItemWithDirections.removeClass(\"expanded\")\n $(this).addClass(\"expanded\")\n }\n })\n\n $viewMoreSearches.off(\"click\")\n $viewMoreSearches.click((e) => {\n e.preventDefault()\n _limit += 10\n rerender()\n })\n\n return self\n }\n\n function isSongVersionPresent(projectSearchItem, comparativeIds) {\n if (comparativeIds.current_round_sv_ids.includes(_songVersionId)) {\n checkIfSongVersionPresentInCurrentRound(projectSearchItem, comparativeIds)\n } else if (comparativeIds.previous_rounds_sv_ids.includes(_songVersionId)) {\n if (\n !confirm(\"This song version was added to previous rounds. Would you still like to add it?\")\n )\n return true\n } else if (comparativeIds.all_song_ids.includes(_songId)) {\n if (\n !confirm(\n \"Another version of this song was added to the search. Would you still like to add it?\"\n )\n )\n return true\n } else {\n return false\n }\n }\n\n function checkIfSongVersionPresentInCurrentRound(projectSearchItem, comparativeIds) {\n if (!comparativeIds.current_round_srsv_users[_songVersionId].includes(mm.user.attributes.id)) {\n if (!confirm(\"Another user added this song to this round. Would you still like to add it?\"))\n return true\n } else {\n onError(projectSearchItem, \"You have already selected this song for the round.\")\n return true\n }\n }\n\n function enableFrom() {\n _spinner.stop()\n $inputs.removeAttr(\"disabled\")\n $form.removeClass(\"processing\")\n }\n\n function handleCloseErrorMessage(el) {\n el.find(\".close-error-message\").one(\"click\", function (e) {\n e.preventDefault()\n $(this).closest(\".search-error-overlay\").removeClass(\"show\")\n })\n }\n\n function onError(projectSearchItem, message) {\n enableFrom()\n handleCloseErrorMessage(projectSearchItem)\n projectSearchItem.find(\".message-text\").text(message)\n projectSearchItem.find(\".search-error-overlay\").addClass(\"show\")\n }\n\n function onFailure(e) {\n console.error(e)\n }\n\n function onSubmit(e) {\n e.preventDefault()\n if ($name.val() === \"\") return false\n var data = {\n name: $name.val(),\n description: $description.val(),\n }\n\n if (_songVersionId) {\n data.song_version = _songVersionId\n }\n\n $name.off(\"keyup keydown\")\n var projectSearch = mm.ProjectSearch(data)\n\n _spinner.spin(_spinTarg)\n $inputs.attr(\"disabled\", true)\n $form.addClass(\"processing\")\n\n mm.user.project_searches.add(projectSearch)\n projectSearch.create().done(onSuccess).fail(onFailure).always(alwaysRun)\n }\n\n function onSuccess(response) {\n enableFrom()\n $addToSearchOrDirection.off(\"click\")\n\n // a successful response is an HTML partial as a string\n if (typeof response === \"string\" || response.success) {\n _songVersionId = undefined\n mm.drawer.delay = false\n mm.drawerProxy.trigger(\"projectSearches\")\n } else {\n var errorsLength = response.errors.length\n for (var i = 0; i < errorsLength; i++) {\n $(\"\" + response.errors[i] + \"
\")\n .appendTo($(\".search.errors\"))\n .delay(4000)\n .queue(function () {\n $(this).remove()\n })\n }\n mm.drawer.spin(false)\n }\n }\n\n function rerender() {\n if (!self.$el.hasClass(\"showing\")) _limit = 10\n $.ajax({\n url: \"/api/v1/project_searches/user\",\n data: { style: \"benchlist\", limit: _limit },\n success: function (data) {\n var dataHTML = $(data)\n if ($(\"li\", dataHTML).length < _limit) $viewMoreSearches.addClass(\"hide\")\n $(\".list\", self.$el).html(data)\n build()\n },\n })\n }\n\n var superShow = self.show\n self.show = function (payload) {\n rerender()\n superShow()\n _songVersionId = payload.id\n _songId = payload.song_id\n }\n\n return build()\n}\n","/**\n * Scripts specific to the drawer register page\n * @public\n * @return {Object} Instance of mm.drawerRegisterPage (Singleton)\n */\n\nmm.DrawerRegisterPage = function () {\n var self = mm.DrawerPage($(\".mm-drawer-wrap #register\")),\n form = mm.DrawerForm($(\".mm-drawer-form\", self.$el), {\n url: \"/register\",\n type: \"POST\",\n empty: true,\n }),\n newsletterCheckbox = $(\"#newsletter\", self.$el),\n termsCheckbox = $(\"#agree_to_terms\", self.$el),\n registerButton = $(\"#create-account button\", self.$el)\n\n // Set newsletter opt-in to true if checked, false if not\n newsletterCheckbox.on(\"change\", function () {\n $(this).val(this.checked)\n $(this).attr(\"aria-checked\", this.checked)\n })\n\n termsCheckbox.on(\"change\", function () {\n registerButton.prop(\"disabled\", !this.checked)\n })\n\n var superHide = self.hide\n self.hide = function () {\n superHide()\n form.reset()\n }\n\n var superShow = self.show\n self.show = function () {\n superShow()\n form.reset()\n }\n\n return self\n}\n","/**\n * payload expects tp be { type: '{song version|mixtape}', id: id }\n */\n\nmm.DrawerSharePage = function () {\n \"use strict\"\n\n var self = mm.DrawerPage($(\".mm-drawer-wrap #share\")),\n $choices = $(\"[data-action]\", self.$el),\n _event = {},\n _data,\n _form,\n _url\n\n var actions = {\n /**\n * Fallback email option, opens native mail program\n * @private\n * @param {Object} e The click event\n */\n\n email: function (e) {\n _event.label = \"email\"\n var $a = $(\"\", {\n href: \"mailto:\" + recipient + \"?\" + $.param(params),\n }),\n params = {\n subject: \"Marmoset Music\",\n body: _url,\n }\n $a.one(\"click\", mm.share.email).trigger(\"click\")\n self.trigger(\"complete\")\n },\n\n facebook: function (e) {\n _event.label = \"facebook\"\n\n FB.ui(\n {\n method: \"feed\",\n link: _url,\n },\n function (response) {\n if (response && _.has(response, \"post_id\") && response.post_id) {\n mm.facade.trigger(\"success\", \"Facebook Share\")\n } else {\n return mm.facade.trigger(\"error\", \"Facebook Share\")\n }\n }\n )\n\n self.trigger(\"complete\")\n },\n\n twitter: function (e) {\n _event.label = \"twitter\"\n\n var params = {\n url: _url,\n }\n\n switch (_data.type) {\n case \"mixtape\":\n $.extend(params, {\n text: \"I made a mixtape for you.\",\n hashtags: \"musicwithheart\",\n via: \"marmosetmusic\",\n })\n break\n\n case \"song version\":\n $.extend(params, {\n text: \"This. Song. On Repeat.\",\n hashtags: \"musicwithheart\",\n via: \"marmosetmusic\",\n })\n break\n\n default:\n break\n }\n\n /**\n * build anchor for unobtrusive twitter lib\n */\n\n var $a = $(\"\", {\n href: \"https://twitter.com/intent/tweet?\" + $.param(params),\n })\n\n /**\n * made the twitter intent handler a public function, we only need to bind it\n * then to trigger it\n */\n\n $a.one(\"click\", mm.share.twitter).trigger(\"click\")\n\n self.trigger(\"complete\")\n },\n\n copy: function (e) {\n _event.label = \"copy\"\n var $button = $(\".copy\", self.$el)\n $button.one(\"click\", copyLinkToClipboard(e, _event)).trigger(\"click\")\n alertCopier()\n },\n }\n\n function fullUrl() {\n return \"http://\" + [window.document.location.host, _data.segment, _data.id].join(\"/\")\n }\n\n function copyLinkToClipboard(e, _event) {\n e.preventDefault()\n var category = _event.category\n\n var tempTextArea = document.createElement(\"textarea\")\n tempTextArea.style.position = \"fixed\"\n tempTextArea.style.opacity = 0\n tempTextArea.value = _url\n document.body.appendChild(tempTextArea)\n tempTextArea.select()\n document.execCommand(\"Copy\")\n document.body.removeChild(tempTextArea)\n }\n\n function alertCopier() {\n var $button = $(\".copy\", self.$el)\n $button.addClass(\"copied\")\n }\n\n function bindChoices() {\n $choices.off(\"click\").on(\"click\", function (e) {\n e.preventDefault()\n var action = $(this).attr(\"data-action\")\n actions[action] = _.bind(actions[action], this, e)\n actions[action]()\n return false\n })\n }\n\n /**\n * Builds the share email form for both mixtapes and song versions\n * @private\n */\n\n function buildForm() {\n _form = mm.DrawerForm($(\".mm-drawer-form\", self.$el), {\n url: [\"\", _data.api, _data.id, \"share\"].join(\"/\"),\n type: \"POST\",\n empty: true,\n })\n }\n\n self.on(\"complete\", function () {\n _data = null\n mm.drawerProxy.trigger(\"close\")\n })\n\n var superHide = self.hide\n self.hide = function () {\n superHide()\n if (_form) {\n _form.reset()\n }\n }\n\n var superShow = self.show\n self.show = function (payload) {\n bindChoices()\n superShow()\n if (_form) {\n _form.reset()\n }\n }\n\n self.on(\"shareData\", function (payload) {\n _data = payload\n\n self.hideNested()\n\n $(\".copy\", self.$el).removeClass(\"copied\")\n\n _event.action = \"share\"\n _event.value = _data.id\n\n switch (_data.type) {\n case \"mixtape\":\n _event.category = \"mixtape\"\n _event.value = _data.internalID\n self.$el.find(\".email.mixtape\").show().siblings(\".email\").hide()\n buildForm()\n break\n case \"song version\":\n _event.category = \"song version\"\n self.$el.find(\".email.song-version\").show().siblings(\".email\").hide()\n buildForm()\n break\n default:\n self.$el.find(\".email.default\").show().siblings(\".email\").hide()\n break\n }\n\n _url = fullUrl()\n })\n\n return self\n}\n","/**\n * The messageCenter is used as a central point of communication to handle the\n * displaying of messages within a modal - this object is necessary to ensure\n * messages do not become 'stacked' on top of eachother.\n * @constructor (singleton)\n * @extends {mm.EventEmitter}\n * @public\n * @return {Object} mm.messageCenter\n */\nmm.messageCenter = (function () {\n var self = mm.EventEmitter()\n\n /**\n * Initializer, creates props\n * @public\n * @return {Object} mm.messageCenter\n */\n self.init = function () {\n self.$el = $(\"#message-center\").attr(\"tabindex\", -1)\n // TODO: replace oak logic here\n // self.__el = oak.strap(self.$el[0])\n self.$close = $(\".close\", self.$el)\n self.$cover = $(\".cover\", self.$el)\n self.$target = $(\".target\", self.$el)\n\n self.$close.on(\"click\", function (e) {\n e.preventDefault()\n self.close()\n })\n\n self.$cover.on(\"click\", function (e) {\n e.preventDefault()\n self.close()\n })\n\n self.$el.on(\"keydown\", function (e) {\n if (e.keyCode === 27) {\n e.preventDefault()\n self.close()\n }\n })\n\n return self\n }\n\n /**\n * Checks the queue for an available message and calls to display it\n * @private\n */\n function checkQueue() {\n var message = self.queue.shift()\n if (message) {\n display(message)\n }\n }\n\n function display(message) {\n var message_id = message.content.attr(\"class\")\n\n if (message_id === \"playerWrapper\") {\n self.$el.addClass(\"videoPlayerContainer\")\n }\n\n self.$target.html(message.content)\n self.$el.addClass(\"showing\").removeClass(\"hiding\").attr(\"data-id\", message_id).focus()\n\n self.$target.find(\"#closeModal\").on(\"click\", function (e) {\n e.preventDefault()\n self.close()\n })\n\n if (message_id === \"playerWrapper\") {\n self.$target.find(\".iframeWrapper iframe\").on(\"load\", function () {\n self.$target.find(\".playerWrapper .loading\").addClass(\"hidden\")\n })\n }\n\n if (typeof message.callback === \"function\") {\n message.callback()\n }\n checkQueue()\n }\n\n /**\n * Queue of mm.Message instances to display\n * @type {Array}\n */\n self.queue = []\n\n self.close = function () {\n self.$el.addClass(\"hiding\").removeClass(\"showing\")\n self.$el[0].addEventListener(\n \"transitionend\",\n function onClose() {\n self.$el.removeClass(\"hiding\")\n self.$el[0].className = \"\"\n self.$close[0].className = \"close\"\n self.$target.html(\"\")\n self.$el[0].removeEventListener(\"transitionend\", onClose)\n },\n false\n )\n if ($(\"div.past-licenses.disabled\").length > 0) {\n $(\"div.past-licenses\").removeClass(\"disabled\")\n }\n }\n\n /**\n * Accepts a message and calls to check the queue\n * @public\n * @param {mm.Message} message An instance of mm.Message\n */\n self.read = function (message) {\n // if there is no message, exit.\n if (typeof message === \"undefined\") return\n self.queue.push(message)\n checkQueue()\n }\n\n return self\n})()\n\nmm.facade.on(\"app:ready\", mm.messageCenter.init)\n","mm.mobilePlayer = (function () {\n \"use strict\"\n\n var self = mm.BasePlayer($(\".player.mobile\"))\n\n var superInit = self.init\n self.init = function () {\n superInit()\n self.streamWidth = self.$songVersionStream.width()\n self.sizeElapsed()\n\n $(\"nav[role=main] .player-toggle\").off(\"click\").on(\"click\", togglePlay)\n }\n\n function togglePlay() {\n if (self.isState(\"playing\")) {\n self.pause()\n } else if (self.isState(\"paused\")) {\n self.play()\n }\n }\n\n self._onCanplay = function (e) {\n mm.spin(false)\n self.$playButton.removeClass(\"spinning\")\n self.audio.play()\n }\n\n var superOnPlaying = self.states.onPlaying\n self.states.onPlaying = function () {\n superOnPlaying()\n self.$el.add(self.$el.parents(\"li.player\")).addClass(\"expanded\")\n $(\"nav[role=main] .hamburger\").addClass(\"music-playing\")\n $(\"nav[role=main]\").removeClass(\"paused\")\n $(\"nav[role=main]\").addClass(\"playing\")\n }\n\n var superOnPaused = self.states.onPaused\n self.states.onPaused = function () {\n superOnPaused()\n $(\"nav[role=main] .hamburger\").removeClass(\"music-playing\")\n $(\"nav[role=main]\").removeClass(\"playing\")\n $(\"nav[role=main]\").addClass(\"paused\")\n }\n\n var superOnIdle = self.states.onIdle\n self.states.onIdle = function () {\n superOnIdle()\n $(\"nav[role=main]\").removeClass(\"playing paused\")\n }\n\n return self\n})()\n\nmm.facade.on(\"app:ready\", function () {\n if (mm.isMobile) mm.mobilePlayer.init()\n})\n","/*!\n * JavaScript Cookie v2.1.3\n * https://github.com/js-cookie/js-cookie\n *\n * Copyright 2006, 2015 Klaus Hartl & Fagner Brack\n * Released under the MIT license\n */\n;(function (factory) {\n var registeredInModuleLoader = false;\n if (typeof define === 'function' && define.amd) {\n define(factory);\n registeredInModuleLoader = true;\n }\n if (typeof exports === 'object') {\n module.exports = factory();\n registeredInModuleLoader = true;\n }\n if (!registeredInModuleLoader) {\n var OldCookies = window.Cookies;\n var api = window.Cookies = factory();\n api.noConflict = function () {\n window.Cookies = OldCookies;\n return api;\n };\n }\n}(function () {\n function extend () {\n var i = 0;\n var result = {};\n for (; i < arguments.length; i++) {\n var attributes = arguments[ i ];\n for (var key in attributes) {\n result[key] = attributes[key];\n }\n }\n return result;\n }\n\n function init (converter) {\n function api (key, value, attributes) {\n var result;\n if (typeof document === 'undefined') {\n return;\n }\n\n // Write\n\n if (arguments.length > 1) {\n attributes = extend({\n path: '/'\n }, api.defaults, attributes);\n\n if (typeof attributes.expires === 'number') {\n var expires = new Date();\n expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);\n attributes.expires = expires;\n }\n\n try {\n result = JSON.stringify(value);\n if (/^[\\{\\[]/.test(result)) {\n value = result;\n }\n } catch (e) {}\n\n if (!converter.write) {\n value = encodeURIComponent(String(value))\n .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);\n } else {\n value = converter.write(value, key);\n }\n\n key = encodeURIComponent(String(key));\n key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);\n key = key.replace(/[\\(\\)]/g, escape);\n\n return (document.cookie = [\n key, '=', value,\n attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE\n attributes.path ? '; path=' + attributes.path : '',\n attributes.domain ? '; domain=' + attributes.domain : '',\n attributes.secure ? '; secure' : ''\n ].join(''));\n }\n\n // Read\n\n if (!key) {\n result = {};\n }\n\n // To prevent the for loop in the first place assign an empty array\n // in case there are no cookies at all. Also prevents odd result when\n // calling \"get()\"\n var cookies = document.cookie ? document.cookie.split('; ') : [];\n var rdecode = /(%[0-9A-Z]{2})+/g;\n var i = 0;\n\n for (; i < cookies.length; i++) {\n var parts = cookies[i].split('=');\n var cookie = parts.slice(1).join('=');\n\n if (cookie.charAt(0) === '\"') {\n cookie = cookie.slice(1, -1);\n }\n\n try {\n var name = parts[0].replace(rdecode, decodeURIComponent);\n cookie = converter.read ?\n converter.read(cookie, name) : converter(cookie, name) ||\n cookie.replace(rdecode, decodeURIComponent);\n\n if (this.json) {\n try {\n cookie = JSON.parse(cookie);\n } catch (e) {}\n }\n\n if (key === name) {\n result = cookie;\n break;\n }\n\n if (!key) {\n result[name] = cookie;\n }\n } catch (e) {}\n }\n\n return result;\n }\n\n api.set = api;\n api.get = function (key) {\n return api.call(api, key);\n };\n api.getJSON = function () {\n return api.apply({\n json: true\n }, [].slice.call(arguments));\n };\n api.defaults = {};\n\n api.remove = function (key, attributes) {\n api(key, '', extend(attributes, {\n expires: -1\n }));\n };\n\n api.withConverter = init;\n\n return api;\n }\n\n return init(function () {});\n}));\n","(function(t,e){if(typeof exports==\"object\")module.exports=e();else if(typeof define==\"function\"&&define.amd)define(e);else t.Spinner=e()})(this,function(){\"use strict\";var t=[\"webkit\",\"Moz\",\"ms\",\"O\"],e={},i;function o(t,e){var i=document.createElement(t||\"div\"),o;for(o in e)i[o]=e[o];return i}function n(t){for(var e=1,i=arguments.length;e>1):parseInt(n.left,10)+s)+\"px\",top:(n.top==\"auto\"?l.y-a.y+(t.offsetHeight>>1):parseInt(n.top,10)+s)+\"px\"})}r.setAttribute(\"role\",\"progressbar\");e.lines(r,e.opts);if(!i){var u=0,p=(n.lines-1)*(1-n.direction)/2,c,h=n.fps,m=h/n.speed,y=(1-n.opacity)/(m*n.trail/100),g=m/n.lines;(function v(){u++;for(var t=0;t>1)+\"px\"})}for(;r',e)}r.addRule(\".spin-vml\",\"behavior:url(#default#VML)\");p.prototype.lines=function(e,i){var o=i.length+i.width,r=2*o;function s(){return f(t(\"group\",{coordsize:r+\" \"+r,coordorigin:-o+\" \"+-o}),{width:r,height:r})}var a=-(i.width+i.length)*2+\"px\",l=f(s(),{position:\"absolute\",top:a,left:a}),d;function u(e,r,a){n(l,n(f(s(),{rotation:360/i.lines*e+\"deg\",left:~~r}),n(f(t(\"roundrect\",{arcsize:i.corners}),{width:o,height:i.width,left:i.radius,top:-i.width>>1,filter:a}),t(\"fill\",{color:i.color,opacity:i.opacity}),t(\"stroke\",{opacity:0}))))}if(i.shadow)for(d=1;d<=i.lines;d++)u(d,-2,\"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)\");for(d=1;d<=i.lines;d++)u(d);return n(e,l)};p.prototype.opacity=function(t,e,i,o){var n=t.firstChild;o=o.shadow&&o.lines||0;if(n&&e+o