1/* exported FMPlaylist */ 2var FMPlaylist = ( function( $ ) { 3 'use strict'; 4 5 /** 6 * Playlist wrapper function. 7 */ 8 var playlist = function( element, data ) { 9 this.element = element; 10 this.data = data; 11 this.options = $.extend( true, {}, this.defaults, element.data( 'playlist' ) ); 12 this.init(); 13 }; 14 15 /** 16 * Playlist prototype. 17 */ 18 playlist.prototype = { 19 20 /** 21 * Options defaults. 22 */ 23 defaults: { 24 autoUpdate: false, 25 autoUpdateSpeed: 60 * 1000, // 60 seconds. 26 delay: false, // set delay in ms if multiple versions of same widget exist on a page. 27 type: 'playlist', 28 ajaxOptions: { 29 subtype: '', 30 limit: '' 31 } 32 }, 33 34 /** 35 * Holds data. 36 */ 37 data: {}, 38 39 /** 40 * Holds options. 41 */ 42 options: {}, 43 44 /** 45 * Holds the element. 46 */ 47 element: null, 48 49 /** 50 * Current song title. 51 */ 52 currentSongTitle: null, 53 54 /** 55 * Constructor. 56 */ 57 constructor: playlist, 58 59 /** 60 * Initialize playlist. 61 */ 62 init: function() { 63 var self = this; 64 65 if ( ! self.options.delay ) { 66 self.render(); 67 } else { 68 setTimeout(function() { 69 self.render(); 70 }, self.options.delay); 71 } 72 73 if ( true === self.options.autoUpdate ) { 74 self.setAutoUpdate(); 75 } 76 77 if ( 'playlist-music' === self.options.type ) { 78 self.bindChangeEvents(); 79 } 80 }, 81 82 /** 83 * Set the playlist auto updater. 84 */ 85 setAutoUpdate: function() { 86 setInterval( this.render.bind( this ), this.options.autoUpdateSpeed ); 87 }, 88 89 /** 90 * Bind the dropdown change events on the playlist page (used for browsing previous days playists). 91 */ 92 bindChangeEvents: function() { 93 var self = this; 94 95 $( '#playlist-select' ).on( 'change', function() { 96 var suffix = $( this ).val(); 97 98 if ( '-1' !== suffix ) { 99 $( '.c-heading--playlist' ).text( $( this ).find( 'option:selected' ).text() ); 100 self.element.addClass( 'l-list--loading' ); 101 self.element.html( '' ); 102 self.render( self.data.endpoint[ self.options.type ].basepath + '_' + suffix + '.js' ); 103 } 104 }) 105 }, 106 107 populateTemplate: function( element, templateName, response ) { 108 var self = this; 109 var template = _.template( $( '#' + templateName ).html() ); 110 var html = template({ 'data': response, 'opts': self.options.ajaxOptions }); 111 element.html( html ); 112 element.trigger( 'fm-' + self.options.type + ':rendered' ); 113 element.removeClass( 'l-list--loading' ); 114 element.removeClass( 'l-list--error' ); 115 }, 116 117 /** 118 * Fetch and render playlist, and conditionally update the song title. 119 */ 120 render: function( playlist_url ) { 121 var self = this; 122 var url = ( playlist_url ) ? playlist_url : self.data.endpoint[ self.options.type ].url; 123 124 if ( url.indexOf('pl_long') > 0 ) { 125 // prepend today's short playlist to the long playlist, which is sometimes out of date 126 self.renderLong( url ); 127 } else { 128 $.ajax({ 129 url: url, 130 dataType: 'jsonp', 131 jsonpCallback: self.data.endpoint[ self.options.type ].callback, 132 timeout: 7000 133 }).always( function( response ) { 134 if ( undefined !== response ) { 135 var template = self.data.endpoint[ self.options.type ].template; 136 if ( 'string' === typeof( template ) ) { 137 self.populateTemplate( self.element, template, response ); 138 } else { 139 var elementIds = Object.keys( template ); 140 for ( var i = 0; i < elementIds.length; i++ ) { 141 var elementId = elementIds[i]; 142 var elementTemplate = template[elementId]; 143 var $targetElem = $( '#' + elementId ); 144 if ( $targetElem.length > 0 ) { 145 self.populateTemplate( $( '#' + elementId ), elementTemplate, response ); 146 } 147 } 148 } 149 } 150 }).fail( function() { 151 self.element.addClass( 'l-list--error' ); 152 self.element.text("Sorry! We can't find any playlist information for this day."); 153 }); 154 } 155 }, 156 157 /** 158 * Fetch and render long playlist, prepending the short playlist which is more up to date, and conditionally update the song title. 159 */ 160 renderLong: function( long_playlist_url ) { 161 var self = this; 162 var short_playlist_url = self.data.endpoint.playlist.url; 163 164 $.ajax({ 165 url: short_playlist_url, 166 dataType: 'jsonp', 167 jsonpCallback: self.data.endpoint.playlist.callback, 168 timeout: 7000 169 }).always( function( responseShort ) { 170 $.ajax({ 171 url: long_playlist_url, 172 dataType: 'jsonp',
173 jsonpCallback: self.data.endpoint[ self.options.type ].callback, 174 timeout: 7000 175 }).always( function( responseLong ) { 176 if ( undefined !== responseShort && responseShort.songs && undefined !== responseLong && responseLong.songs ) { 177 // concat arrays and remove duplicates 178 var songs = responseShort.songs.concat(responseLong.songs); 179 songs = ( undefined !== _.uniqBy ) ? _.uniqBy( songs, 'date' ) : _.uniq( songs, 'date' ); 180 songs = { "songs": songs }; 181 182 self.populateTemplate( self.element, self.data.endpoint[ self.options.type ].template, songs ); 183 } 184 }).fail( function() { 185 self.element.addClass( 'l-list--error' ); 186 self.element.text("Sorry! We can't find any playlist information for this day."); 187 }); 188 }).fail( function() { 189 self.element.addClass( 'l-list--error' ); 190 self.element.text("Sorry! We can't find any playlist information for this day."); 191 }); 192 } 193 }; 194 195 return { 196 197 /** 198 * Boot playlists from the DOM elements. 199 */ 200 boot: function( data ) { 201 $( document ).ready( function() { 202 $( '[data-playlist]' ).each( function() { 203 new playlist( $( this ), data ); 204 }); 205 }); 206 } 207 }; 208})( window.jQuery ); 209
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.