MrSiir

Whatsnew Spotify App Code

Dec 6th, 2011
2,876
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. "use strict";
  2.  
  3. sp = getSpotifyApi(1);
  4.  
  5. exports.init = init;
  6.  
  7. var storage = sp.require('sp://import/scripts/storage'),
  8.     schema = sp.require('sp://import/hermes/discovery'),
  9.     util = sp.require('sp://import/scripts/util'),
  10.     dom = sp.require('sp://import/scripts/dom'),
  11.     array = sp.require('sp://import/scripts/array'),
  12.     fx = sp.require('sp://import/scripts/fx'),
  13.     ui = sp.require('sp://import/scripts/ui'),
  14.     cf = sp.require('sp://import/scripts/coverflow'),
  15.     lang = sp.require('sp://import/scripts/language'),
  16.     catalog = lang.loadCatalog('cef_views'),
  17.     _ = partial(lang.getString, catalog, "What's New"),
  18.     p = sp.require('sp://import/scripts/pager'),
  19.     r = sp.require('sp://import/scripts/react'),
  20.     m = sp.require("sp://import/scripts/api/models"),
  21.     v = sp.require("sp://import/scripts/api/views"),
  22.     presence = sp.require("sp://import/scripts/presence");
  23.  
  24. var headings = {
  25.     FriendsPlaylists: _('sFriendsPlaylistsA')+' <span>'+_('sFriendsPlaylistsB')+'</span>',
  26.     RegionPlaylists: _('sRegionPlaylistsA')+' <span>'+_('sRegionPlaylistsB')+'</span>',
  27.     FriendsTracks: _('sFriendsTracksA')+' <span>'+_('sFriendsTracksB')+'</span>',
  28.     RegionTracks: _('sRegionTracksA')+' <span>'+_('sRegionTracksB')+'</span>',
  29.     NewFriends: _('sNewFriendsA')+' <span>'+_('sNewFriendsB')+'</span>',
  30.     NewReleases: _('sNewReleases'),
  31.     CountryTracks: _('sCountryTracks'),
  32.     CountryPlaylists: _('sCountryPlaylists')
  33. };
  34.  
  35. var loadingEl = null, hiddenSections = {}, currentStep = -1, currentLayout = 0,
  36.     playlistToplists, trackToplists, playlistWrappers, trackWrappers,
  37.     failures = [];
  38.  
  39. /**
  40.  * Initiate the page
  41.  */
  42. function init()
  43. {
  44.     loadingEl = dom.queryOne('.loading');
  45.     if (!navigator.onLine) {
  46.         goOffline();
  47.         return;
  48.     }
  49.     playlistToplists = [FriendsPlaylists, RegionPlaylists, CountryPlaylists];
  50.     trackToplists = [FriendsTracks, RegionTracks, CountryTracks];
  51.     playlistWrappers = [
  52.         dom.queryOne('#topToplists .playlists'),
  53.         dom.queryOne('#bottomToplists .playlists')
  54.     ];
  55.     trackWrappers = [
  56.         dom.queryOne('#topToplists .tracks'),
  57.         dom.queryOne('#bottomToplists .tracks')
  58.     ];
  59.  
  60.     setLayoutType();
  61.  
  62.     dom.listen(window, 'resize', function() {
  63.         setLayoutType();
  64.     });
  65.  
  66.     Data.init();
  67.     Discovery.init();
  68.     NewReleases.init();
  69.     CountryTracks.init();
  70.     CountryPlaylists.init();
  71.     Ads.init();
  72. }
  73.  
  74.  
  75.  
  76. /**
  77.  *
  78.  */
  79. function goOffline()
  80. {
  81.     // If we go online again, reboot the page
  82.     dom.listen(window, 'online', function(e) {
  83.         window.location.reload();
  84.     });
  85.     dom.destroy(loadingEl);
  86.     dom.id('wrapper').style.display = 'none';
  87.  
  88.     var offlineEl = new dom.Element('p', {id: 'offline', text: _('sUnavailableOffline')});
  89.     dom.inject(offlineEl, document.body);
  90. }
  91.  
  92.  
  93.  
  94. /**
  95.  *
  96.  */
  97. function triggerFailure(key)
  98. {
  99.     if (failures.indexOf(key) === -1) {
  100.         failures.push(key);
  101.     }
  102.  
  103.     if (failures.length === 3) {
  104.         goOffline();
  105.     }
  106. }
  107.  
  108.  
  109.  
  110. /**
  111.  *
  112.  */
  113. function hasVScroll()
  114. {
  115.     if (window.scrollY > 0) {
  116.         return true;
  117.     }
  118.     return window.innerHeight < document.height;
  119. }
  120.  
  121.  
  122.  
  123. /**
  124.  *
  125.  */
  126. function setLayoutType()
  127. {
  128.     var scrollWidth = hasVScroll() ? 14 : 0,
  129.         w = window.innerWidth - scrollWidth, newLayout = currentLayout;
  130.     if (w > 800) {
  131.         newLayout = 1;
  132.     } else if (w > 650 && w <= 800) {
  133.         newLayout = 2;
  134.     } else if (w <= 650) {
  135.         newLayout = 3;
  136.     }
  137.  
  138.     if (currentLayout !== newLayout) {
  139.         currentLayout = newLayout;
  140.         new dom.Event('layout.switch', true).dispatch(window);
  141.     }
  142. }
  143.  
  144.  
  145.  
  146. /**
  147.  *
  148.  */
  149. function checkMPU(cb)
  150. {
  151.     if (Ads.isLoaded()) {
  152.         cb.call(null, Ads.hasMPU());
  153.     } else {
  154.         dom.listen(window, 'ads.load', function() {
  155.             cb.call(null, Ads.hasMPU());
  156.         });
  157.     }
  158. }
  159.  
  160.  
  161.  
  162. /**
  163.  *
  164.  */
  165. function step(sectionLoaded)
  166. {
  167.     // console.log('STEP: ', currentStep, sectionLoaded);
  168.  
  169.     switch (currentStep) {
  170.         case -1:
  171.             currentStep++;
  172.             NewAlbums.next();
  173.             break;
  174.         case 0: // NewAlbums
  175.             if (!sectionLoaded) {
  176.                 dom.destroy(dom.id('NewAlbums'));
  177.             }
  178.             currentStep++;
  179.             playlistToplists.shift().next();
  180.             break;
  181.         case 1: // Top toplists, left
  182.             if (!sectionLoaded && !playlistToplists.length) {
  183.                 dom.destroy(playlistWrappers.shift());
  184.             }
  185.             if (!sectionLoaded && playlistToplists.length) {
  186.                 playlistToplists.shift().next();
  187.             } else {
  188.                 checkMPU(function(hasMPU) {
  189.                     currentStep++;
  190.                     if (!hasMPU) {
  191.                         trackToplists.shift().next();
  192.                     } else {
  193.                         dom.listen(window, 'ads.build', function() {
  194.                             step(true);
  195.                         });
  196.                     }
  197.                     Ads.next();
  198.                 });
  199.             }
  200.             break;
  201.         case 2: // Top toplists, right
  202.             if (!sectionLoaded && !trackToplists.length) {
  203.                 dom.destroy(trackWrappers.shift());
  204.             }
  205.             if (!sectionLoaded && trackToplists.length) {
  206.                 trackToplists.shift().next();
  207.             } else {
  208.                 currentStep++;
  209.                 NewFriends.next();
  210.             }
  211.             break;
  212.         case 3: // New friends
  213.             if (!sectionLoaded) {
  214.                 dom.destroy(dom.id('NewFriends'));
  215.             }
  216.             currentStep++;
  217.             NewReleases.next();
  218.             break;
  219.         case 4: // New releases
  220.             if (!sectionLoaded) {
  221.                 dom.destroy(dom.id('NewReleases'));
  222.             }
  223.             if(playlistToplists.length) {
  224.                 currentStep++;
  225.                 playlistToplists.shift().next();
  226.             }
  227.             else {
  228.                 currentStep++;
  229.             }
  230.             break;
  231.         case 5: // Bottom toplists, left
  232.             if (!sectionLoaded && !playlistToplists.length) {
  233.                 dom.destroy(playlistWrappers.shift());
  234.             }
  235.             if (!sectionLoaded && playlistToplists.length) {
  236.                 playlistToplists.shift().next();
  237.             } else {
  238.                 if(trackToplists.length) {
  239.                     currentStep++;
  240.                     trackToplists.shift().next();
  241.                 }
  242.                 else {
  243.                     currentStep++;
  244.                     step(false)
  245.                 }
  246.             }
  247.             break;
  248.         case 6: // Bottom toplists, right
  249.             if (!sectionLoaded && !trackToplists.length) {
  250.                 dom.destroy(trackWrappers.shift());
  251.             }
  252.             if (!sectionLoaded && trackToplists.length) {
  253.                 trackToplists.shift().next();
  254.             } else {
  255.                 console.log('Done loading! GREAT SUCCESS!');
  256.             }
  257.             break;
  258.     }
  259. }
  260.  
  261.  
  262.  
  263. /**
  264.  *
  265.  */
  266. var Data = {
  267.  
  268.     _cachedData: {},
  269.     _liveData: {},
  270.  
  271.     /**
  272.      *
  273.      */
  274.     init: function()
  275.     {
  276.         // this._cachedData = storage.getWithDefault('whatsnewData', {});
  277.     },
  278.  
  279.     /**
  280.      *
  281.      */
  282.     set: function(key, value)
  283.     {
  284.         this._liveData[key] = value;
  285.         //this.store();
  286.     },
  287.  
  288.     /**
  289.      * Get data by key
  290.      */
  291.     get: function(key)
  292.     {
  293.         if (this._liveData[key]) {
  294.             return this._liveData[key];
  295.         } else if (this._cachedData[key]) {
  296.             return this._cachedData[key];
  297.         }
  298.         return false;
  299.     },
  300.  
  301.     /**
  302.      * Check if data exists for a key
  303.      */
  304.     has: function(key)
  305.     {
  306.         if (this._liveData[key] || this._cachedData[key]) {
  307.             return true;
  308.         }
  309.         return false;
  310.     },
  311.  
  312.     /**
  313.      * Merge cached and live data and cache it
  314.      */
  315.     store: function()
  316.     {
  317.         storage.set('whatsnewData', util.merge({}, this._cachedData, this._liveData));
  318.     }
  319. };
  320.  
  321.  
  322.  
  323. /**
  324.  *
  325.  */
  326. var Discovery = {
  327.  
  328.     _loaded: false,
  329.     _loadEvent: null,
  330.  
  331.     /**
  332.      *
  333.      */
  334.     init: function()
  335.     {
  336.         this._loadEvent = new dom.Event('discovery.load', true);
  337.  
  338.         var useCache = setTimeout(function() {
  339.             triggerFailure('Discovery');
  340.         }, 2000);
  341.  
  342.         var self = this;
  343.         sp.core.getHermes('GET', 'hm://discovery/get-whats-new-data/', [], {
  344.             onSuccess: function(message)
  345.             {
  346.                 clearTimeout(useCache);
  347.                 var data = sp.core.parseHermesReply('WhatsNewReply', message);
  348.  
  349.                 NewAlbums.init(data.new_albums);
  350.                 FriendsPlaylists.init(data.friends_playlists);
  351.                 FriendsTracks.init(data.friends_tracks);
  352.                 RegionPlaylists.init(data.region_playlists);
  353.                 RegionTracks.init(data.region_tracks);
  354.                 NewFriends.init(data.new_friends);
  355.  
  356.                 self._loaded = true;
  357.                 self._loadEvent.dispatch(window);
  358.                 step(true);
  359.             },
  360.             onFailure: function(errorCode)
  361.             {
  362.                 triggerFailure('Discovery');
  363.             }
  364.         });
  365.     },
  366.  
  367.     /**
  368.      *
  369.      */
  370.     isLoaded: function()
  371.     {
  372.         return this._loaded;
  373.     }
  374. };
  375.  
  376.  
  377.  
  378. /**
  379.  *
  380.  */
  381. var NewAlbums = {
  382.  
  383.     _key: 'NewAlbums',
  384.     _loaded: false,
  385.     _loadEvent: null,
  386.  
  387.     /**
  388.      *
  389.      */
  390.     init: function(data)
  391.     {
  392.         this._loadEvent = new dom.Event(this._key+'.load', true);
  393.         if (!data || !data.albums) {
  394.             data = {albums: []};
  395.         }
  396.         this.data = data;
  397.         this.padAlbums = [];
  398.         this.pad();
  399.     },
  400.  
  401.     /**
  402.      *
  403.      */
  404.     pad: function()
  405.     {
  406.         var self = this;
  407.         sp.social.getToplist("album", sp.core.country, sp.core.user.canonicalUsername, {
  408.             onSuccess: function(result) {
  409.                 if (result.albums && result.albums.length) {
  410.                     for (var i = 0; i < 50; i++) {
  411.                         var tmp = {};
  412.                         tmp["album_uri"] = result.albums[i].uri;
  413.                         tmp.isRecommended = false;
  414.                         self.data.albums.push(tmp);
  415.                     }
  416.                 }
  417.                 self.extend(self.data.albums);
  418.             },
  419.             onFailure: function() {
  420.                 self._loaded = true;
  421.                 self._loadEvent.dispatch(window);
  422.             }
  423.         });
  424.     },
  425.  
  426.     /**
  427.      * Extend data received from discovery
  428.      */
  429.     extend: function(data)
  430.     {
  431.         var self = this, uris = [];
  432.         data.forEach(function(album) {
  433.             uris.push(album.album_uri);
  434.         });
  435.         var count = 0;
  436.         var recommendedCount = 0;
  437.         var toplistCount = 0;
  438.  
  439.         if (!uris.length) {
  440.             this._loaded = true;
  441.             this._loadEvent.dispatch(window);
  442.             return;
  443.         }
  444.  
  445.         sp.core.getMetadata(uris, {
  446.             onSuccess: function(md)
  447.             {
  448.                 var filteredMetadata = [], artists = [];
  449.                 for(var i=0;i<md.length;i++) {
  450.                     var d = md[i];
  451.                     //Make sure we only include unique and available albums
  452.                     if(!array.contains(artists,d.artist.name) && d.artist.name !== "Various Artists" && d.availableForPlayback) {
  453.                         artists.push(d.artist.name);
  454.                         //Set isRecommended or not
  455.                         data.forEach(function(aData,index) {
  456.                             if(aData.album_uri == d.uri) {
  457.                                 if(aData.isRecommended !== false) {
  458.                                     d.isRecommended = true;
  459.                                     recommendedCount++;
  460.                                 }
  461.                                 else {
  462.                                     d.isRecommended = false;
  463.                                     toplistCount++;
  464.                                 }
  465.                             }
  466.                         });
  467.                         //Clone and delete album tracks array
  468.                         var clonedAlbum = clone(d);
  469.                         clonedAlbum.tracks = [];
  470.                         filteredMetadata.push(clonedAlbum);
  471.                         if(count == 4) {
  472.                             break;
  473.                         }
  474.                         count++;
  475.                     }
  476.                 }
  477.                 filteredMetadata = array.shuffle(filteredMetadata);
  478.                 if(!filteredMetadata[2].isRecommended) {
  479.                     for(var i = 0;i<filteredMetadata.length;i++){
  480.                         if(filteredMetadata[i].isRecommended) {
  481.                             var tempI = filteredMetadata[i];
  482.                             var tempJ = filteredMetadata[2];
  483.                             filteredMetadata[2] = tempI;
  484.                             filteredMetadata[i] = tempJ;
  485.                             break;
  486.                         }
  487.                     }
  488.                 }
  489.                 Data.set(self._key,filteredMetadata);
  490.                 self._loaded = true;
  491.                 self._loadEvent.dispatch(window);
  492.             },
  493.             onFailure: function()
  494.             {
  495.                 self._loaded = true;
  496.                 self._loadEvent.dispatch(window);
  497.             }
  498.         });
  499.     },
  500.  
  501.     /**
  502.      *
  503.      */
  504.     next: function()
  505.     {
  506.         if (this._loaded) {
  507.             this.build();
  508.         } else {
  509.             dom.listen(window, this._key+'.load', this.build.bind(this));
  510.         }
  511.     },
  512.  
  513.     /**
  514.      *
  515.      */
  516.     build: function()
  517.     {
  518.         if (!Data.has(this._key)) {
  519.             step(false);
  520.             return;
  521.         }
  522.         if (loadingEl) {
  523.             dom.destroy(loadingEl);
  524.         }
  525.  
  526.         var coverflow = new cf.Coverflow(this, {itemCount:5});
  527.         dom.inject(coverflow.node, dom.id('NewAlbums'));
  528.  
  529.         step(true);
  530.     },
  531.  
  532.     /**
  533.      *
  534.      */
  535.     count: function()
  536.     {
  537.         return Data.get(this._key).length;
  538.     },
  539.  
  540.     /**
  541.      *
  542.      */
  543.     makeNode: function(index)
  544.     {
  545.         var data = Data.get(this._key)[index],
  546.             li = new dom.Element('li'),
  547.             badge = new dom.Element('div', {className: 'badge', textContent: (data.isRecommended ? 'Recommended' : 'Top list')}),
  548.             md = new dom.Element('div', {className: 'metadata'}),
  549.             album = new dom.Element('a', {className: 'album', href: data.uri, html: data.name}),
  550.             artist = new dom.Element('a', {className: 'artist', href: data.artist.uri, html: data.artist.name});
  551.  
  552.         m.Album.fromURI(data.uri, function(context) {
  553.             var player = new v.Player();
  554.             player.context = context;
  555.             var playerbutton = player.node.querySelector("button");
  556.             var oldchild = player.node.removeChild(playerbutton);
  557.             dom.adopt(md, oldchild, album, document.createElement("br"), artist);
  558.             dom.adopt(player.node, badge, md);
  559.             dom.adopt(li, player.node);
  560.         });
  561.  
  562.         return li;
  563.     }
  564. };
  565.  
  566.  
  567.  
  568. /**
  569.  *
  570.  */
  571. var FriendsPlaylists = {
  572.  
  573.     _key: 'FriendsPlaylists',
  574.     _loaded: false,
  575.     _loadEvent: null,
  576.  
  577.     /**
  578.      *
  579.      */
  580.     init: function(data)
  581.     {
  582.         this._loadEvent = new dom.Event(this._key+'.load', true);
  583.  
  584.         if (!data || !data.playlists) {
  585.             this._loaded = true;
  586.             this._loadEvent.dispatch(window);
  587.             return;
  588.         }
  589.  
  590.         this.extend(data.playlists);
  591.     },
  592.  
  593.     /**
  594.      *
  595.      */
  596.     extend: function(data)
  597.     {
  598.         var final = [];
  599.         for (var i = 0; i < data.length; i++) {
  600.             if (data[i].name) {
  601.                 final.push(data[i]);
  602.             }
  603.         }
  604.  
  605.         array.shuffle(final);
  606.  
  607.         Data.set(this._key, final);
  608.         this._loaded = true;
  609.         this._loadEvent.dispatch(window);
  610.     },
  611.  
  612.     /**
  613.      *
  614.      */
  615.     next: function()
  616.     {
  617.         if (this._loaded) {
  618.             this.build();
  619.         } else {
  620.             dom.listen(window, this._key+'.load', this.build.bind(this));
  621.         }
  622.     },
  623.  
  624.     /**
  625.      *
  626.      */
  627.     build: function()
  628.     {
  629.         if (!Data.has(this._key)) {
  630.             step(false);
  631.             return;
  632.         }
  633.         if (loadingEl) {
  634.             dom.destroy(loadingEl);
  635.         }
  636.  
  637.         buildPlaylistPager(this._key, Data.get(this._key), playlistWrappers.shift(), function() {
  638.             step(true);
  639.         });
  640.     }
  641. };
  642.  
  643.  
  644.  
  645. /**
  646.  *
  647.  */
  648. var FriendsTracks = {
  649.  
  650.     _key: 'FriendsTracks',
  651.     _loaded: false,
  652.     _loadEvent: null,
  653.  
  654.     /**
  655.      *
  656.      */
  657.     init: function(data)
  658.     {
  659.         this. _loadEvent = new dom.Event(this._key+'.load', true);
  660.  
  661.         if (!data || !data.tracks) {
  662.             this._loaded = true;
  663.             this._loadEvent.dispatch(window);
  664.             return;
  665.         }
  666.  
  667.         this.extend(data.tracks);
  668.     },
  669.  
  670.     /**
  671.      *
  672.      */
  673.     extend: function(data)
  674.     {
  675.         var self = this;
  676.         sp.core.getMetadata(map(function(track) { return track.uri; }, data), {
  677.             onSuccess: function(metadata) {
  678.                 for (var i = 0; i < data.length; i++) {
  679.                     data[i].metadata = metadata[i];
  680.                 }
  681.                 array.shuffle(data);
  682.  
  683.                 Data.set(self._key, data);
  684.                 self._loaded = true;
  685.                 self._loadEvent.dispatch(window);
  686.             },
  687.             onFailure: function() {
  688.                 self._loaded = true;
  689.                 self._loadEvent.dispatch(window);
  690.             }
  691.         });
  692.     },
  693.  
  694.     /**
  695.      *
  696.      */
  697.     next: function()
  698.     {
  699.         if (this._loaded) {
  700.             this.build();
  701.         } else {
  702.             dom.listen(window, this._key+'.load', this.build.bind(this));
  703.         }
  704.     },
  705.  
  706.     /**
  707.      *
  708.      */
  709.     build: function()
  710.     {
  711.         if (!Data.has(this._key)) {
  712.             step(false);
  713.             return;
  714.         }
  715.         if (loadingEl) {
  716.             dom.destroy(loadingEl);
  717.         }
  718.  
  719.         buildTracksTable(this._key, Data.get(this._key), trackWrappers.shift(), function() {
  720.             step(true);
  721.         });
  722.     }
  723. };
  724.  
  725.  
  726.  
  727. /**
  728.  *
  729.  */
  730. var NewFriends = {
  731.  
  732.     _key: 'NewFriends',
  733.     _loaded: false,
  734.     _loadEvent: null,
  735.  
  736.     /**
  737.      *
  738.      */
  739.     init: function(data)
  740.     {
  741.         this._loadEvent = new dom.Event(this._key+'.load', true);
  742.  
  743.         if (!data || !data.friends) {
  744.             this._loaded = true;
  745.             this._loadEvent.dispatch(window);
  746.             return;
  747.         }
  748.  
  749.         this.extend(data.friends);
  750.     },
  751.  
  752.     /**
  753.      *
  754.      */
  755.     extend: function(data)
  756.     {
  757.         var existingUserNames = [];
  758.         var filteredData = [];
  759.         data.forEach(function(username,index) {
  760.             var userExists = false;
  761.             for (var i=0;i<existingUserNames.length;i++) {
  762.                 if (existingUserNames[i] == username) {
  763.                     userExists = true;
  764.                 }
  765.             }
  766.             if (!userExists) {
  767.                 existingUserNames.push(username);
  768.                 filteredData.push(username);
  769.             }
  770.         });
  771.  
  772.         this._data = filteredData;
  773.         array.shuffle(filteredData);
  774.         Data.set(this._key, filteredData);
  775.         this._loaded = true;
  776.         this._loadEvent.dispatch(window);
  777.     },
  778.  
  779.     /**
  780.      *
  781.      */
  782.     next: function()
  783.     {
  784.         if (this._loaded) {
  785.             this.build();
  786.         } else {
  787.             dom.listen(window, this._key+'.load', this.build.bind(this));
  788.         }
  789.     },
  790.  
  791.     /**
  792.      *
  793.      */
  794.     build: function()
  795.     {
  796.         if (!Data.has(this._key)) {
  797.             step(false);
  798.             return;
  799.         }
  800.         if (loadingEl) {
  801.             dom.destroy(loadingEl);
  802.         }
  803.  
  804.         var self = this;
  805.  
  806.         var wrapper = dom.id(this._key),
  807.             allFriends = Data.get(this._key),
  808.             data = [],
  809.             friendsAvailable = allFriends.length,
  810.             friendsLoaded = 0;
  811.  
  812.         if (!wrapper || !allFriends) {
  813.             return;
  814.         }
  815.  
  816.         var _friendsLoadEvent = new dom.Event('newfriendsdata.load', true);
  817.  
  818.         //When all friends are loaded, build:
  819.         dom.listen(window,'newfriendsdata.load',function(evt) {
  820.             if(data.length > 0) {
  821.                 var perPage = 4;
  822.  
  823.                 switch (currentLayout) {
  824.                     case 1: perPage = 4; break;
  825.                     case 2: perPage = 3; break;
  826.                     case 3: perPage = 2; break;
  827.                 }
  828.  
  829.                 var pager = new p.Pager(new newFriendsDataSource(data), {
  830.                     perPage: perPage,
  831.                     hidePartials: false,
  832.                     orientation: 'horizontal',
  833.                     pagingLocation: 'top',
  834.                     bullets: false
  835.                 });
  836.                 if (loadingEl) {
  837.                     dom.destroy(loadingEl);
  838.                 }
  839.                 pager.h2.innerHTML = headings[self._key];
  840.                 dom.adopt(wrapper, pager.node);
  841.  
  842.                 step(true);
  843.             }
  844.             else {
  845.                 step(false);
  846.             }
  847.  
  848.             dom.listen(window, 'layout.switch', function() {
  849.                 var perPage = null
  850.                 switch (currentLayout) {
  851.                     case 1: perPage = 4; break;
  852.                     case 2: perPage = 3; break;
  853.                     case 3: perPage = 2; break;
  854.                 }
  855.                 if (perPage) {
  856.                     pager.setOptions({perPage: perPage});
  857.                     pager.reflow();
  858.                 }
  859.             });
  860.         });
  861.  
  862.         //Loop through allFriends and only get those with a facebook UID
  863.         allFriends.forEach(function(friend) {
  864.             sp.social.getUserByUsername(friend, {
  865.                 onSuccess: function(user) {
  866.                     if (user.facebookUid) {
  867.                         friendsLoaded++;
  868.                         data.push(user);
  869.                     }
  870.                     else {
  871.                         friendsAvailable--;
  872.                     }
  873.                     if(friendsAvailable == friendsLoaded) {
  874.                         _friendsLoadEvent.dispatch(window);
  875.                     }
  876.                 },
  877.                 onFailure:function(){
  878.                     friendsAvailable--;
  879.                     if(friendsAvailable == friendsLoaded) {
  880.                         _friendsLoadEvent.dispatch(window);
  881.                     }
  882.                 }
  883.             });
  884.         });
  885.     }
  886. };
  887.  
  888.  
  889.  
  890. /**
  891.  *
  892.  */
  893. var NewReleases = {
  894.  
  895.     _key: 'NewReleases',
  896.     _loaded: false,
  897.     _loadEvent: null,
  898.  
  899.     /**
  900.      *
  901.      */
  902.     init: function()
  903.     {
  904.         this._loadEvent = new dom.Event(this._key+'.load', true);
  905.  
  906.         var self = this, artists = [], albums = [];
  907.  
  908.         var useCache = setTimeout(function() {
  909.             triggerFailure('Search');
  910.         }, 2000);
  911.  
  912.         sp.core.search('tag:new', {
  913.             onSuccess: function (result) {
  914.                 clearTimeout(useCache);
  915.                 // Make sure artists are unique
  916.                 result.albums.forEach(function(album) {
  917.                     if (album.artist.name !== "Various Artists" && artists.indexOf(album.artist.name) === -1) {
  918.                         artists.push(album.artist.name);
  919.                         albums.push(album);
  920.                     }
  921.                 });
  922.                 array.shuffle(albums);
  923.  
  924.                 Data.set(self._key, albums);
  925.                 self._loaded = true;
  926.                 self._loadEvent.dispatch(window);
  927.             },
  928.             onFailure: function() {
  929.                 triggerFailure('Search');
  930.                 self._loaded = true;
  931.                 self._loadEvent.dispatch(window);
  932.                 // step(false);
  933.             }
  934.         });
  935.     },
  936.  
  937.     /**
  938.      *
  939.      */
  940.     next: function()
  941.     {
  942.         if (this._loaded) {
  943.             this.build();
  944.         } else {
  945.             dom.listen(window, this._key+'.load', this.build.bind(this));
  946.         }
  947.     },
  948.  
  949.     /**
  950.      *
  951.      */
  952.     build: function()
  953.     {
  954.         if (!Data.has(this._key)) {
  955.             step(false);
  956.             return;
  957.         }
  958.         if (loadingEl) {
  959.             dom.destroy(loadingEl);
  960.         }
  961.  
  962.         var wrapper = dom.id(this._key), perPage = 10;
  963.  
  964.         switch (currentLayout) {
  965.             case 1: perPage = 10; break;
  966.             case 2: perPage = 8; break;
  967.             case 3: perPage = 6; break;
  968.         }
  969.  
  970.         var pager = new p.Pager(new newReleasesDataSource(), {
  971.             perPage: perPage,
  972.             hidePartials: true,
  973.             pagingLocation: 'top',
  974.             bullets: false
  975.         });
  976.         pager.h2.innerHTML = headings[this._key];
  977.  
  978.         dom.adopt(wrapper, pager.node);
  979.  
  980.         dom.listen(window, 'layout.switch', function() {
  981.             var perPage = null;
  982.             switch (currentLayout) {
  983.                 case 1: perPage = 10; break;
  984.                 case 2: perPage = 8; break;
  985.                 case 3: perPage = 6; break;
  986.             }
  987.             if (perPage) {
  988.                 pager.setOptions({perPage: perPage});
  989.                 pager.reflow();
  990.             }
  991.         });
  992.  
  993.         step(true);
  994.     }
  995. };
  996.  
  997.  
  998.  
  999. /**
  1000.  *
  1001.  */
  1002. var RegionPlaylists = {
  1003.  
  1004.     _key: 'RegionPlaylists',
  1005.     _loaded: false,
  1006.     _loadEvent: null,
  1007.  
  1008.     /**
  1009.      *
  1010.      */
  1011.     init: function(data)
  1012.     {
  1013.         this._loadEvent = new dom.Event(this._key+'.load', true);
  1014.  
  1015.         if (!data || !data.playlists) {
  1016.             this._loaded = true;
  1017.             this._loadEvent.dispatch(window);
  1018.             return;
  1019.         }
  1020.  
  1021.         this.extend(data.playlists);
  1022.     },
  1023.  
  1024.     /**
  1025.      *
  1026.      */
  1027.     extend: function(data)
  1028.     {
  1029.         var final = [];
  1030.         for (var i = 0; i < data.length; i++) {
  1031.             if (data[i].name) {
  1032.                 final.push(data[i]);
  1033.             }
  1034.         }
  1035.         array.shuffle(final);
  1036.         Data.set('RegionPlaylists', final);
  1037.         this._loaded = true;
  1038.         this._loadEvent.dispatch(window);
  1039.     },
  1040.  
  1041.     /**
  1042.      *
  1043.      */
  1044.     next: function()
  1045.     {
  1046.         if (this._loaded) {
  1047.             this.build();
  1048.         } else {
  1049.             dom.listen(window, this._key+'.load', this.build.bind(this));
  1050.         }
  1051.     },
  1052.  
  1053.     /**
  1054.      *
  1055.      */
  1056.     build: function()
  1057.     {
  1058.         if (!Data.has(this._key)) {
  1059.             step(false);
  1060.             return;
  1061.         }
  1062.         if (loadingEl) {
  1063.             dom.destroy(loadingEl);
  1064.         }
  1065.  
  1066.         buildPlaylistPager(this._key, Data.get(this._key), playlistWrappers.shift(), function() {
  1067.             step(true);
  1068.         });
  1069.     }
  1070. };
  1071.  
  1072.  
  1073.  
  1074. /**
  1075.  *
  1076.  */
  1077. var RegionTracks = {
  1078.  
  1079.     _key: 'RegionTracks',
  1080.     _loaded: false,
  1081.     _loadEvent: null,
  1082.  
  1083.     /**
  1084.      *
  1085.      */
  1086.     init: function(data)
  1087.     {
  1088.         this._loadEvent = new dom.Event(this._key+'.load', true);
  1089.  
  1090.         if (!data || !data.tracks) {
  1091.             this._loaded = true;
  1092.             this._loadEvent.dispatch(window);
  1093.             return;
  1094.         }
  1095.  
  1096.         this.extend(data.tracks);
  1097.     },
  1098.  
  1099.     /**
  1100.      *
  1101.      */
  1102.     extend: function(data)
  1103.     {
  1104.         var self = this;
  1105.         sp.core.getMetadata(map(function(track) { return track.uri; }, data), {
  1106.             onSuccess: function(metadata) {
  1107.                 for (var i = 0; i < data.length; i++) {
  1108.                     data[i].metadata = metadata[i];
  1109.                 }
  1110.                 array.shuffle(data);
  1111.                 Data.set('RegionTracks', data);
  1112.                 self._loaded = true;
  1113.                 self._loadEvent.dispatch(window);
  1114.             },
  1115.             onFailure: function() {
  1116.                 self._loaded = true;
  1117.                 self._loadEvent.dispatch(window);
  1118.             }
  1119.         });
  1120.     },
  1121.  
  1122.     /**
  1123.      *
  1124.      */
  1125.     next: function()
  1126.     {
  1127.         if (this._loaded) {
  1128.             this.build();
  1129.         } else {
  1130.             dom.listen(window, this._key+'.load', this.build.bind(this));
  1131.         }
  1132.     },
  1133.  
  1134.     /**
  1135.      *
  1136.      */
  1137.     build: function()
  1138.     {
  1139.         if (!Data.has(this._key)) {
  1140.             step(false);
  1141.             return;
  1142.         }
  1143.         if (loadingEl) {
  1144.             dom.destroy(loadingEl);
  1145.         }
  1146.  
  1147.         buildTracksTable(this._key, Data.get(this._key), trackWrappers.shift(), function() {
  1148.             step(true);
  1149.         });
  1150.     }
  1151. };
  1152.  
  1153.  
  1154.  
  1155. /**
  1156.  *
  1157.  */
  1158. var CountryPlaylists = {
  1159.  
  1160.     _key: 'CountryPlaylists',
  1161.     _loaded: false,
  1162.     _loadEvent: null,
  1163.  
  1164.     /**
  1165.      *
  1166.      */
  1167.     init: function(data)
  1168.     {
  1169.         this._loaded = true;
  1170.  
  1171.         /*
  1172.         this._loadEvent = new dom.Event(this._key+'.load', true);
  1173.  
  1174.         if (!data || !data.playlists) {
  1175.             this._loaded = true;
  1176.             this._loadEvent.dispatch(window);
  1177.             return;
  1178.         }
  1179.  
  1180.         this.extend(data.playlists);
  1181.         */
  1182.     },
  1183.  
  1184.     /**
  1185.      *
  1186.      */
  1187.     extend: function(data)
  1188.     {
  1189.         /*
  1190.         var final = [];
  1191.         for (var i = 0; i < data.length; i++) {
  1192.             if (data[i].name) {
  1193.                 final.push(data[i]);
  1194.             }
  1195.         }
  1196.  
  1197.         Data.set('RegionPlaylists', final);
  1198.         this._loaded = true;
  1199.         this._loadEvent.dispatch(window);
  1200.         */
  1201.     },
  1202.  
  1203.     /**
  1204.      *
  1205.      */
  1206.     next: function()
  1207.     {
  1208.         if (this._loaded) {
  1209.             this.build();
  1210.             // if (Data.has(this._key)) {
  1211.             // } else {
  1212.             // }
  1213.         } else {
  1214.             step(false);
  1215.             //dom.listen(window, this._key+'.load', this.build.bind(this));
  1216.         }
  1217.     },
  1218.  
  1219.     /**
  1220.      *
  1221.      */
  1222.     build: function()
  1223.     {
  1224.         if (!Data.has(this._key)) {
  1225.             step(false);
  1226.             return;
  1227.         }
  1228.         if (loadingEl) {
  1229.             dom.destroy(loadingEl);
  1230.         }
  1231.  
  1232.         /*
  1233.         This element is currently empty because of missing Discovery data
  1234.         We're manually adding the key as a class to the wrapper so we can adjust the margin
  1235.         */
  1236.         if(playlistWrappers.length > 0) {
  1237.             playlistWrappers[0].classList.add('CountryPlaylists')
  1238.         }
  1239.         //playlistWrappers.shift().classList.add('CountryPlaylists');
  1240.         //dom.id(playlistWrappers.shift())
  1241.         step(true);
  1242.         /*
  1243.         buildPlaylistPager(this._key, Data.get(this._key), playlistWrappers.shift(), function() {
  1244.             step(true);
  1245.         });
  1246.         */
  1247.     }
  1248. };
  1249.  
  1250.  
  1251.  
  1252. /**
  1253.  *
  1254.  */
  1255. var CountryTracks = {
  1256.  
  1257.     _key: 'CountryTracks',
  1258.     _loaded: false,
  1259.     _loadEvent: null,
  1260.  
  1261.     /**
  1262.      *
  1263.      */
  1264.     init: function()
  1265.     {
  1266.         var self = this;
  1267.         this._loadEvent = new dom.Event(this._key+'.load', true);
  1268.  
  1269.         var useCache = setTimeout(function() {
  1270.             triggerFailure('Country');
  1271.         }, 2000);
  1272.  
  1273.         sp.social.getToplist("track", sp.core.country, sp.core.user.canonicalUsername, {
  1274.             onSuccess: function(result) {
  1275.                 clearTimeout(useCache);
  1276.  
  1277.                 if(result.tracks && result.tracks.length) {
  1278.                     array.shuffle(result.tracks);
  1279.                     Data.set(self._key, result.tracks);
  1280.                     self._loaded = true;
  1281.                     self._loadEvent.dispatch(window);
  1282.                 }
  1283.                 else {
  1284.                     self._loaded = true;
  1285.                     self._loadEvent.dispatch(window);
  1286.                 }
  1287.             },
  1288.             onFailure: function() {
  1289.                 self._loaded = true;
  1290.                 self._loadEvent.dispatch(window);
  1291.                 triggerFailure('Country');
  1292.             }
  1293.         });
  1294.     },
  1295.  
  1296.     /**
  1297.      *
  1298.      */
  1299.     next: function()
  1300.     {
  1301.         if (this._loaded) {
  1302.             this.build();
  1303.         } else {
  1304.             dom.listen(window, this._key+'.load', this.build.bind(this));
  1305.         }
  1306.     },
  1307.  
  1308.     /**
  1309.      *
  1310.      */
  1311.     build: function()
  1312.     {
  1313.         if (!Data.has(this._key)) {
  1314.             step(false);
  1315.             return;
  1316.         }
  1317.         if (loadingEl) {
  1318.             dom.destroy(loadingEl);
  1319.         }
  1320.  
  1321.         var d = Data.get(this._key);
  1322.         d.forEach(function(track,index){
  1323.             track.metadata = track;
  1324.         })
  1325.  
  1326.         buildTracksTable(this._key, d, trackWrappers.shift(), function() {
  1327.             step(true);
  1328.         });
  1329.     }
  1330. };
  1331.  
  1332.  
  1333.  
  1334. /**
  1335.  *
  1336.  */
  1337. var Ads = {
  1338.  
  1339.     _loaded: false,
  1340.     _loadEvent: null,
  1341.     ad: null,
  1342.     partner: null,
  1343.  
  1344.     /**
  1345.      *
  1346.      */
  1347.     init: function()
  1348.     {
  1349.         var self = this;
  1350.         self._loadEvent = new dom.Event('ads.load', true);
  1351.  
  1352.         var partner = sp.whatsnew.getPartner();
  1353.         if (partner && partner === 'telia') {
  1354.             this.partner = partner;
  1355.         }
  1356.  
  1357.         sp.whatsnew.fetchAd({
  1358.             onSuccess: function(ad)
  1359.             {
  1360.                 self.ad = ad;
  1361.  
  1362.                 // Let things know we're done loading
  1363.                 self._loaded = true;
  1364.                 self._loadEvent.dispatch(window);
  1365.             },
  1366.             onFailure: function(errorCode) {
  1367.                 self._loaded = true;
  1368.                 self._loadEvent.dispatch(window);
  1369.             }
  1370.         });
  1371.     },
  1372.  
  1373.     /**
  1374.      *
  1375.      */
  1376.     isLoaded: function()
  1377.     {
  1378.         return this._loaded;
  1379.     },
  1380.  
  1381.     /**
  1382.      *
  1383.      */
  1384.     hasMPU: function()
  1385.     {
  1386.         if (this.ad) {
  1387.             if (!this.ad.bg) {
  1388.                 return true;
  1389.             }
  1390.             return false;
  1391.         }
  1392.  
  1393.         if (this.partner) {
  1394.             return true;
  1395.         }
  1396.     },
  1397.  
  1398.     /**
  1399.      *
  1400.      */
  1401.     next: function()
  1402.     {
  1403.         if (this._loaded) {
  1404.             this.build();
  1405.         } else {
  1406.             dom.listen(window, 'ads.load', this.build.bind(this));
  1407.         }
  1408.     },
  1409.  
  1410.     /**
  1411.      *
  1412.      */
  1413.     build: function()
  1414.     {
  1415.         var hasAd = false;
  1416.         // Ad
  1417.         if (this.ad) {
  1418.             // A background at least means it's shown at the top
  1419.             if (this.ad.bg) {
  1420.                 hasAd = true;
  1421.                 if (this.ad.bg.target && this.ad.bg.target !== '') {
  1422.                     var bg = new dom.Element('a', {href: this.ad.bg.target});
  1423.                 } else {
  1424.                     var bg = new dom.Element('div');
  1425.                 }
  1426.                 bg.style.backgroundImage = 'url('+this.ad.bg.image_uri+')';
  1427.                 bg.className = 'ad-bg';
  1428.                 dom.adopt(document.body, bg);
  1429.                 document.body.classList.add('has-ad-bg');
  1430.             }
  1431.  
  1432.             if (this.ad.mpu && (this.ad.mpu.html || this.ad.mpu.image)) {
  1433.                 hasAd = true;
  1434.                 var mpuWrapper = new dom.Element('div');
  1435.  
  1436.                 if (this.ad.mpu.html) {
  1437.                     var frame = new dom.Element('iframe');
  1438.                     frame.src = "data:text/html;charset=utf-8;base64," + btoa(this.ad.mpu.html);
  1439.                     frame.scrolling = "no";
  1440.                     dom.inject(frame, mpuWrapper);
  1441.                 }
  1442.  
  1443.                 else if (this.ad.mpu.image) {
  1444.                     if (this.ad.mpu.image.target && this.ad.mpu.image.target !== '') {
  1445.                         var imgHolder = new dom.Element('a', {href: this.ad.mpu.image.target});
  1446.                     } else {
  1447.                         var imgHolder = new dom.Element('div');
  1448.                     }
  1449.                     var img = new Image();
  1450.                     img.src = this.ad.mpu.image.image_uri;
  1451.                     dom.inject(img, imgHolder);
  1452.                     dom.inject(imgHolder, mpuWrapper);
  1453.                 }
  1454.  
  1455.                 if (bg) {
  1456.                     mpuWrapper.className = 'topWrapper';
  1457.                     dom.inject(mpuWrapper, dom.id('wrapper'), 'top');
  1458.                     document.body.classList.add('has-ad-top');
  1459.                 } else {
  1460.                     mpuWrapper.className = 'mpuWrapper';
  1461.                     dom.inject(mpuWrapper, trackWrappers.shift());
  1462.                     document.body.classList.add('has-ad-mpu');
  1463.                 }
  1464.             }
  1465.  
  1466.             if (hasAd) {
  1467.                 sp.whatsnew.reportAdStarted();
  1468.  
  1469.                 sp.core.addEventListener('activate',function(){
  1470.                     sp.whatsnew.reportAdStarted();
  1471.                 });
  1472.  
  1473.                 sp.core.addEventListener('deactivate',function(){
  1474.                     sp.whatsnew.reportAdStopped();
  1475.                 });
  1476.             }
  1477.         }
  1478.  
  1479.         // Partner
  1480.         else if (this.partner) {
  1481.             hasAd = true;
  1482.             var mpuWrapper = new dom.Element('div', {className: 'mpuWrapper'}),
  1483.                 imgHolder = new dom.Element('div'),
  1484.                 img = new Image();
  1485.             img.src = '/img/telia.png';
  1486.             dom.inject(img, imgHolder);
  1487.             dom.inject(imgHolder, mpuWrapper);
  1488.             dom.inject(mpuWrapper, trackWrappers.shift());
  1489.         }
  1490.  
  1491.         if (hasAd) {
  1492.             new dom.Event('ads.build', true).dispatch(window);
  1493.         }
  1494.     }
  1495. };
  1496.  
  1497.  
  1498.  
  1499.  
  1500.  
  1501.  
  1502.  
  1503.  
  1504.  
  1505.  
  1506.  
  1507.  
  1508.  
  1509.  
  1510.  
  1511.  
  1512. function newToplistPlaylistsDataSource(data,showFriends)
  1513. {
  1514.     var data = data, showFriends = showFriends || false;
  1515.  
  1516.     this.count = function()
  1517.     {
  1518.         return data.length;
  1519.     };
  1520.  
  1521.     this.makeNode = function(index)
  1522.     {
  1523.         var d = data[index], li = new dom.Element('li');
  1524.  
  1525.         var uri = d.uri,
  1526.         name = d.name,
  1527.         creator = d.uri.split(':')[2],
  1528.         creator = d.creator ? d.creator : creator,
  1529.         creatorUri = 'spotify:user:'+d.uri.split(':')[2];
  1530.  
  1531.         var nameColumn = new dom.Element('div',{
  1532.             className:'nameColumn',
  1533.             html:'<div class="nameColumn"><a href="'+uri+'" class="name">'+name+'</a> '+_('sBy')+' <a href="'+creatorUri+'" class="creator">'+creator+'</a>'
  1534.         });
  1535.         dom.adopt(li, nameColumn);
  1536.         if (showFriends) {
  1537.             var friendsColumn = new dom.Element('div', {className: 'friendsColumn'});
  1538.             if (d.friends && d.friends.length > 0) {
  1539.             d.friends.forEach(function(f) {
  1540.                 sp.social.getUserByUsername(f, {
  1541.                     onSuccess: function(fd) {
  1542.                         if (fd.facebookUid) {
  1543.                             dom.adopt(friendsColumn, new ui.SPImage('https://graph.facebook.com/'+fd.facebookUid+'/picture', 'spotify:user:'+fd.canonicalUsername, fd.name).node);
  1544.                         }
  1545.                     }
  1546.                 });
  1547.             });
  1548.             }
  1549.             dom.adopt(li,friendsColumn);
  1550.         }
  1551.         return li;
  1552.     };
  1553. }
  1554.  
  1555. function newFriendsDataSource(data)
  1556. {
  1557.     var data = data;
  1558.  
  1559.     this.count = function()
  1560.     {
  1561.         return data.length;
  1562.     };
  1563.  
  1564.     this.makeNode = function(index)
  1565.     {
  1566.         var d = data[index], li = new dom.Element('li');
  1567.  
  1568.         li.innerHTML = '<a href="spotify:user:'+d.canonicalUsername+'" class="image" '
  1569.             +'style="background-image: url(https://graph.facebook.com/'+d.facebookUid+'/picture)"></a>'
  1570.             +'<span class="text"><a href="spotify:user:'+d.canonicalUsername+'" class="user">'+d.name+'</a>'
  1571.             +'<span class="presence"></span></span>';
  1572.  
  1573.         presence.observePresence(d, function(user, presenceString){
  1574.             dom.queryOne('.presence', li).innerHTML = presenceString;
  1575.         });
  1576.  
  1577.         return li;
  1578.     };
  1579. }
  1580.  
  1581. function newReleasesDataSource()
  1582. {
  1583.     var data = Data.get('NewReleases');
  1584.  
  1585.     this.count = function()
  1586.     {
  1587.         return data.length;
  1588.     };
  1589.  
  1590.     this.makeNode = function(index)
  1591.     {
  1592.         var d = data[index], li = new dom.Element('li');
  1593.  
  1594.         var albumLink = new dom.Element('a', {className: 'name', href: d.uri, text: d.name});
  1595.         var artistLink = new dom.Element('a', {className: 'artist', href: d.artist.uri, text: d.artist.name});
  1596.  
  1597.         var album = m.Album.fromURI(d.uri, function(a) {
  1598.             var albumPlayer = new v.Player();
  1599.             albumPlayer.track = a.get(0);
  1600.             albumPlayer.context = a;
  1601.             dom.inject(albumPlayer.node, li, 'top')
  1602.         });
  1603.  
  1604.         dom.adopt(li, albumLink, artistLink);
  1605.         return li;
  1606.     };
  1607. }
  1608.  
  1609. /**
  1610.  *
  1611.  */
  1612. function buildPlaylistPager(key, data, wrapper, callback) {
  1613.     var callback = callback || function() {};
  1614.  
  1615.     if (!data || !wrapper) { return; }
  1616.  
  1617.     var showFriends = data[0].friends ? true : false;
  1618.  
  1619.     var ds = new newToplistPlaylistsDataSource(data,showFriends)
  1620.     var pager = new p.Pager(ds, {perPage: 5, hidePartials: true,orientation:'vertical',pagingLocation:'top',bullets:false,listType:'list'});
  1621.  
  1622.  
  1623.     pager.h2.innerHTML = headings[key];
  1624.     var border = new dom.Element('div',{className:'border'});
  1625.     dom.adopt(pager.node,border);
  1626.     dom.adopt(wrapper,pager.node)
  1627.  
  1628.     callback.call()
  1629.  
  1630.     return pager;
  1631. }
  1632.  
  1633. function buildTracksTable(key, data, wrapper, callback) {
  1634.     var callback = callback || function() {};
  1635.  
  1636.     if (!data || !wrapper) { return; }
  1637.  
  1638.     data = data.slice(0, 8);
  1639.  
  1640.     var tempPlaylist = new m.Playlist();
  1641.     data.forEach(function(d){
  1642.         var t = new m.Track(d);
  1643.         tempPlaylist.add(t);
  1644.     })
  1645.     var tracksList = new v.List(tempPlaylist, function(track) {
  1646.         return new v.Track(track, v.Track.FIELD.STAR | v.Track.FIELD.NAME | v.Track.FIELD.ARTIST);
  1647.     });
  1648.     tracksList._itemHeight = 25;
  1649.     tracksList.node.classList.add('sp-light');
  1650.  
  1651.     var h2 = new dom.Element('h2',{html:headings[key]});
  1652.     var tableHeading = new dom.Element('div',{
  1653.         className:'window',
  1654.         html:'<table><tr class="heading"><th class="headingStarred">'
  1655.             +'</th><th class="headingTitle">'+_('sTitle')+'</th>'
  1656.             +'<th class="headingArtist">'+_('sArtist')+'</th></tr></table>'});
  1657.     dom.adopt(tableHeading,tracksList.node)
  1658.     dom.adopt(wrapper, h2,tableHeading);
  1659.     wrapper.classList.add(key)
  1660.     callback.call();
  1661. }
  1662.  
  1663. function clone(obj) {
  1664.     if(null == obj || 'object' != typeof obj) {
  1665.         return obj;
  1666.     }
  1667.     else if(obj instanceof Array) {
  1668.         var copy = [];
  1669.         for(var i = 0; i < obj.length; i++) {
  1670.             copy[i] = clone(obj[i]);
  1671.         }
  1672.         return copy;
  1673.     }
  1674.     else if(obj instanceof Object) {
  1675.         var copy = {};
  1676.         for(var attr in obj) {
  1677.             if(obj.hasOwnProperty(attr)) {
  1678.                 copy[attr] = clone(obj[attr]);
  1679.             }
  1680.         }
  1681.         return copy;
  1682.     }
  1683.     else {
  1684.         console.log('Unable to clone object of type',typeof obj);
  1685.         return obj;
  1686.     }
  1687. }
Add Comment
Please, Sign In to add comment