Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 5th, 2012  |  syntax: None  |  size: 23.17 KB  |  hits: 27  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*
  2. *   SoundCloud Custom Player jQuery Plugin
  3. *   Author: Matas Petrikas, matas@soundcloud.com
  4. *   Copyright (c) 2009  SoundCloud Ltd.
  5. *   Licensed under the MIT license:
  6. *   http://www.opensource.org/licenses/mit-license.php
  7. *
  8. *   Usage:
  9. *   <a href="http://soundcloud.com/matas/hobnotropic" class="sc-player">My new dub track</a>
  10. *   The link will be automatically replaced by the HTML based player
  11. */
  12. ;(function($) {
  13.   // Convert milliseconds into Hours (h), Minutes (m), and Seconds (s)
  14.   var timecode = function(ms) {
  15.     var hms = function(ms) {
  16.           return {
  17.             h: Math.floor(ms/(60*60*1000)),
  18.             m: Math.floor((ms/60000) % 60),
  19.             s: Math.floor((ms/1000) % 60)
  20.           };
  21.         }(ms),
  22.         tc = []; // Timecode array to be joined with '.'
  23.  
  24.     if (hms.h > 0) {
  25.       tc.push(hms.h);
  26.     }
  27.  
  28.     tc.push((hms.m < 10 && hms.h > 0 ? "0" + hms.m : hms.m));
  29.     tc.push((hms.s < 10  ? "0" + hms.s : hms.s));
  30.  
  31.     return tc.join('.');
  32.   };
  33.  
  34.   var debug = true,
  35.       useSandBox = false,
  36.       log = function(args) {
  37.         if(debug && window.console && window.console.log){
  38.           window.console.log.apply(window.console, arguments);
  39.         }
  40.       },
  41.       domain = useSandBox ? 'sandbox-soundcloud.com' : 'soundcloud.com',
  42.       apiKey = 'htuiRd1JP11Ww0X72T1C3g',
  43.       scApiUrl = function(url) {
  44.         return (/api\./.test(url) ? url + '?' : 'http://api.' + domain +'/resolve?url=' + url + '&') + 'format=json&consumer_key=' + apiKey +'&callback=?';
  45.       };
  46.  
  47.  
  48.   var audioEngine = function() {
  49.     var html5AudioAvailable = function() {
  50.         var state = false;
  51.         try{
  52.           var a = new Audio();
  53.           state = a.canPlayType && (/maybe|probably/).test(a.canPlayType('audio/mpeg'));
  54.           // let's enable the html5 audio on selected mobile devices first, unlikely to support Flash
  55.           // the desktop browsers are still better with Flash, e.g. see the Safari 10.6 bug
  56.           // comment the following line out, if you want to force the html5 mode
  57.           state = state &&  (/iPad|iphone|mobile|pre\//i).test(navigator.userAgent);
  58.         }catch(e){
  59.           // there's no audio support here sadly
  60.         }
  61.         return state;
  62.     }(),
  63.     callbacks = {
  64.       onReady: function() {
  65.         $(document).trigger('scPlayer:onAudioReady');
  66.       },
  67.       onPlay: function() {
  68.         $(document).trigger('scPlayer:onMediaPlay');
  69.       },
  70.       onPause: function() {
  71.         $(document).trigger('scPlayer:onMediaPause');
  72.       },
  73.       onEnd: function() {
  74.         $(document).trigger('scPlayer:onMediaEnd');
  75.       },
  76.       onBuffer: function(percent) {
  77.         $(document).trigger({type: 'scPlayer:onMediaBuffering', percent: percent});
  78.       }
  79.     };
  80.    
  81.     var html5Driver = function() {
  82.       var player = new Audio(),
  83.           onTimeUpdate = function(event){
  84.             var obj = event.target,
  85.                 buffer = ((obj.buffered.length && obj.buffered.end(0)) / obj.duration) * 100;
  86.             // ipad has no progress events implemented yet
  87.             callbacks.onBuffer(buffer);
  88.             // anounce if it's finished for the clients without 'ended' events implementation
  89.             if (obj.currentTime === obj.duration) { callbacks.onEnd(); }
  90.           },
  91.           onProgress = function(event) {
  92.             var obj = event.target,
  93.                 buffer = ((obj.buffered.length && obj.buffered.end(0)) / obj.duration) * 100;
  94.             callbacks.onBuffer(buffer);
  95.           };
  96.        
  97.       $('<div class="sc-player-engine-container"></div>').appendTo(document.body).append(player);
  98.      
  99.       // prepare the listeners
  100.       player.addEventListener('play', callbacks.onPlay, false);
  101.       player.addEventListener('pause', callbacks.onPause, false);
  102.       player.addEventListener('ended', callbacks.onEnd, false);
  103.       player.addEventListener('timeupdate', onTimeUpdate, false);
  104.       player.addEventListener('progress', onProgress, false);
  105.  
  106.      
  107.       return {
  108.         load: function(track) {
  109.           player.pause();
  110.           player.src = track.stream_url;
  111.           player.load();
  112.           player.play();
  113.         },
  114.         play: function() {
  115.           player.play();
  116.         },
  117.         pause: function() {
  118.           player.pause();
  119.         },
  120.         seek: function(relative){
  121.           player.currentTime = player.duration * relative;
  122.           player.play();
  123.         },
  124.         getDuration: function() {
  125.           return player.duration;
  126.         },
  127.         getPosition: function() {
  128.           return player.currentTime;
  129.         },
  130.         setVolume: function(val) {
  131.           if(a){
  132.             a.volume = val / 100;
  133.           }
  134.         }
  135.       };
  136.  
  137.     };
  138.    
  139.  
  140.    
  141.     var flashDriver = function() {
  142.       var engineId = 'scPlayerEngine',
  143.           player,
  144.           flashHtml = function(url) {
  145.             var swf = 'http://player.' + domain +'/player.swf?url=' + url +'&amp;enable_api=true&amp;player_type=engine&amp;object_id=' + engineId;
  146.             if ($.browser.msie) {
  147.               return '<object height="100%" width="100%" id="' + engineId + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" data="' + swf + '">'+
  148.                 '<param name="movie" value="' + swf + '" />'+
  149.                 '<param name="allowscriptaccess" value="always" />'+
  150.                 '</object>';
  151.             } else {
  152.               return '<object height="100%" width="100%" id="' + engineId + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">'+
  153.                 '<embed allowscriptaccess="always" height="100%" width="100%" src="' + swf + '" type="application/x-shockwave-flash" name="' + engineId + '" />'+
  154.                 '</object>';
  155.             }
  156.           };
  157.  
  158.      
  159.      
  160.       // listen to audio engine events
  161.       // when the loaded track is ready to play
  162.       soundcloud.addEventListener('onPlayerReady', function(flashId, data) {
  163.         player = soundcloud.getPlayer(engineId);
  164.         callbacks.onReady();
  165.       });
  166.    
  167.       // when the loaded track finished playing
  168.       soundcloud.addEventListener('onMediaEnd', callbacks.onEnd);
  169.    
  170.       // when the loaded track is still buffering
  171.       soundcloud.addEventListener('onMediaBuffering', function(flashId, data) {
  172.         callbacks.onBuffer(data.percent);
  173.       });
  174.    
  175.       // when the loaded track started to play
  176.       soundcloud.addEventListener('onMediaPlay', callbacks.onPlay);
  177.    
  178.       // when the loaded track is was paused
  179.       soundcloud.addEventListener('onMediaPause', callbacks.onPause);
  180.      
  181.       return {
  182.         load: function(track) {
  183.           var url = track.permalink_url;
  184.           if(player){
  185.             player.api_load(url);
  186.           }else{
  187.             // create a container for the flash engine (IE needs this to operate properly)
  188.             $('<div class="sc-player-engine-container"></div>').appendTo(document.body).html(flashHtml(url));
  189.           }
  190.         },
  191.         play: function() {
  192.           player && player.api_play();
  193.         },
  194.         pause: function() {
  195.           player && player.api_pause();
  196.         },
  197.         seek: function(relative){
  198.           player && player.api_seekTo((player.api_getTrackDuration() * relative));
  199.         },
  200.         getDuration: function() {
  201.           return player && player.api_getTrackDuration && player.api_getTrackDuration();
  202.         },
  203.         getPosition: function() {
  204.           return player && player.api_getTrackPosition && player.api_getTrackPosition();
  205.         },
  206.         setVolume: function(val) {
  207.           if(player && player.api_setVolume){
  208.             player.api_setVolume(val);
  209.           }
  210.         }
  211.  
  212.       };
  213.     };
  214.    
  215.     return html5AudioAvailable? html5Driver() : flashDriver();
  216.  
  217.   }();
  218.  
  219.  
  220.  
  221.       var autoPlay = false,
  222.           players = [],
  223.           updates = {},
  224.           currentUrl,
  225.  
  226.           loadTracksData = function($player, links) {
  227.             var index = 0,
  228.                 playerObj = {node: $player, tracks: []},
  229.                 loadUrl = function(link) {
  230.                   $.getJSON(scApiUrl(link.url), function(data) {
  231.                     // log('data loaded', link.url, data);
  232.                     index += 1;
  233.                     if(data.tracks){
  234.                       // log('data.tracks', data.tracks);
  235.                       playerObj.tracks = playerObj.tracks.concat(data.tracks);
  236.                     }else if(data.duration){
  237.                       // a secret link fix, till the SC API returns permalink with secret on secret response
  238.                       data.permalink_url = link.url;
  239.                       // if track, add to player
  240.                       playerObj.tracks.push(data);
  241.                     }else if(data.username){
  242.                       // if user, get his tracks or favorites
  243.                       if(/favorites/.test(link.url)){
  244.                         links.push({url:data.uri + '/favorites'});
  245.                       }else{
  246.                         links.push({url:data.uri + '/tracks'});
  247.                       }
  248.                     }else if($.isArray(data)){
  249.                       playerObj.tracks = playerObj.tracks.concat(data);
  250.                     }
  251.                     if(links[index]){                    
  252.                       // if there are more track to load, get them from the api
  253.                       loadUrl(links[index]);
  254.                     }else{
  255.                       // if loading finishes, anounce it to the GUI
  256.                       playerObj.node.trigger({type:'onTrackDataLoaded.scPlayer', playerObj: playerObj});
  257.                     }
  258.                  });
  259.                };
  260.             // update the players queue
  261.             players.push(playerObj);
  262.        
  263.             // load first tracks
  264.             loadUrl(links[index]);
  265.           },
  266.           artworkImage = function(track, usePlaceholder) {
  267.             if(usePlaceholder){
  268.               return '<div class="sc-loading-artwork">Loading Artwork</div>';
  269.             }else if (track.artwork_url) {
  270.               return '<img src="' + track.artwork_url.replace('-large', '-t300x300') + '"/>';
  271.             }else{
  272.               return '<div class="sc-no-artwork">No Artwork</div>';
  273.             }
  274.           },
  275.           updateTrackInfo = function($player, track) {
  276.             // update the current track info in the player
  277.             // log('updateTrackInfo', track);
  278.             $('.sc-info', $player).each(function(index) {
  279.               $('h3', this).html('<a href="' + track.permalink_url +'"><font size=4>' + track.title + '</font></a>');
  280.               $('p', this).html(track.description || 'no Description');
  281.             });
  282.             // update the artwork
  283.             $('.sc-artwork-list li', $player).each(function(index) {
  284.               var $item = $(this),
  285.                   itemTrack = $item.data('sc-track');
  286.          
  287.               if (itemTrack === track) {
  288.                 // show track artwork
  289.                 $item
  290.                   .addClass('active')
  291.                   .find('.sc-loading-artwork')
  292.                     .each(function(index) {
  293.                       // if the image isn't loaded yet, do it now
  294.                       $(this).removeClass('sc-loading-artwork').html(artworkImage(track, false));
  295.                     });
  296.               }else{
  297.                 // reset other artworks
  298.                 $item.removeClass('active');
  299.               }
  300.             });
  301.             // cache the references to most updated DOM nodes in the progress bar
  302.             updates = {
  303.               $buffer: $('.sc-buffer', $player),
  304.               $played: $('.sc-played', $player),
  305.               position:  $('.sc-position', $player)[0]
  306.             };
  307.             // update the track duration in the progress bar
  308.             $('.sc-duration', $player).html(timecode(track.duration));
  309.             // put the waveform into the progress bar
  310.             $('.sc-waveform-container', $player).html('<img src="' + track.waveform_url +'" />');
  311.        
  312.             $player.trigger('onPlayerTrackSwitch.scPlayer', [track]);
  313.           },
  314.           play = function(track) {
  315.             var url = track.permalink_url;
  316.             if(currentUrl === url){
  317.               // log('will play');
  318.               audioEngine.play();
  319.             }else{
  320.               currentUrl = url;
  321.               // log('will load', url);
  322.               audioEngine.load(track);
  323.               autoPlay = true;
  324.             }
  325.           },
  326.           getPlayerData = function(node) {
  327.             return players[$(node).data('sc-player').id];
  328.           },
  329.           updatePlayStatus = function(player, status) {
  330.             if(status){
  331.               // reset all other players playing status
  332.               $('div.sc-player.playing').removeClass('playing');
  333.             }
  334.             $(player)
  335.               .toggleClass('playing', status)
  336.               .trigger((status ? 'onPlayerPlay' : 'onPlayerPause') + '.scPlayer');
  337.           },
  338.           onPlay = function(player, id) {
  339.             var track = getPlayerData(player).tracks[id || 0];
  340.             updateTrackInfo(player, track);
  341.             updatePlayStatus(player, true);
  342.             play(track);
  343.           },
  344.           onPause = function(player) {
  345.             updatePlayStatus(player, false);
  346.             audioEngine.pause();
  347.           },
  348.           onSeek = function(player, relative) {
  349.             audioEngine.seek(relative);
  350.           },
  351.           soundVolume = function() {
  352.             var vol = 80,
  353.                 cooks = document.cookie.split(';'),
  354.                 volRx = new RegExp('scPlayer_volume=(\\d+)');
  355.             for(var i in cooks){
  356.               if(volRx.test(cooks[i])){
  357.                 vol = parseInt(cooks[i].match(volRx)[1], 10);
  358.                 break;
  359.               }
  360.             }
  361.             return vol;
  362.           }(),
  363.           onVolume = function(volume) {
  364.             var vol = Math.floor(volume);
  365.             // save the volume in the cookie
  366.             var date = new Date();
  367.             date.setTime(date.getTime() + (365 * 24 * 60 * 60 * 1000));
  368.             soundVolume = vol;
  369.             document.cookie = ['scPlayer_volume=', vol, '; expires=', date.toUTCString(), '; path="/"'].join('');
  370.             // update the volume in the engine
  371.             audioEngine.setVolume(soundVolume);
  372.           },
  373.           positionPoll;
  374.  
  375.     // listen to audio engine events
  376.     $(document)
  377.       .bind('scPlayer:onAudioReady', function(event) {
  378.         log('onPlayerReady: audio engine is ready');
  379.         audioEngine.play();
  380.         // set initial volume
  381.         onVolume(soundVolume);
  382.       })
  383.       // when the loaded track started to play
  384.       .bind('scPlayer:onMediaPlay', function(event) {
  385.         clearInterval(positionPoll);
  386.         positionPoll = setInterval(function() {
  387.           var duration = audioEngine.getDuration() * 1000;
  388.           var position = audioEngine.getPosition();
  389.           updates.$played.css('width', ((position / audioEngine.getDuration()) * 100) + '%');
  390.           updates.position.innerHTML = timecode(position * 1000);
  391.         }, 50);
  392.       })
  393.       // when the loaded track is was paused
  394.       .bind('scPlayer:onMediaPause', function(event) {
  395.         clearInterval(positionPoll);
  396.       })
  397.       // change the volume
  398.       .bind('scPlayer:onVolumeChange', function(event) {
  399.         onVolume(event.volume);
  400.       })
  401.       .bind('scPlayer:onMediaEnd', function(event) {
  402.         log('track finished get the next one');
  403.         if(autoPlay){      
  404.           $('.sc-trackslist li.active').next('li').click();
  405.         }
  406.       })
  407.       .bind('scPlayer:onMediaBuffering', function(event) {
  408.         updates.$buffer.css('width', event.percent + '%');
  409.       });
  410.    
  411.  
  412.   // Generate custom skinnable HTML/CSS/JavaScript based SoundCloud players from links to SoundCloud resources
  413.   $.scPlayer = function(options, node) {
  414.     var opts = $.extend({}, $.fn.scPlayer.defaults, options),
  415.         playerId = players.length,
  416.         $source = node && $(node),
  417.         links = opts.links || $.map($('a', $source).add($source.filter('a')), function(val) { return {url: val.href, title: val.innerHTML}; }),
  418.         $player = $('<div class="sc-player loading"></div>').data('sc-player', {id: playerId}),
  419.         $player = $('<div class="sc-player loading"></div>').data('sc-player', {id: playerId}),
  420.         $artworks = $('<ol class="sc-artwork-list"></ol>').appendTo($player),
  421.         $info = $('<div class="sc-info"><h3></h3><h4></h4><p></p></div>').appendTo($player),
  422.         $controls = $('<div class="sc-controls"></div>').appendTo($player),
  423.         $list = $('<ol class="sc-trackslist"></ol>').appendTo($player);
  424.        
  425.         // enable autoplay if set in the options
  426.         autoPlay = opts.autoPlay;
  427.        
  428.         // adding controls to the player
  429.         $player
  430.           .find('.sc-controls')
  431.             .append('<a href="#play" class="sc-play">Play</a> <a href="#pause" class="sc-pause hidden">Pause</a>')
  432.           .end()
  433.           .append('<div class="sc-scrubber"></div>')
  434.             .find('.sc-scrubber')
  435.               .append('<div class="sc-time-span"><div class="sc-waveform-container"></div><div class="sc-buffer"></div><div class="sc-played"></div></div>')
  436.               .append('<div class="sc-time-indicators"><span class="sc-position"></span> | <span class="sc-duration"></span></div>');
  437.        
  438.         // load and parse the track data from SoundCloud API
  439.         loadTracksData($player, links);
  440.         // init the player GUI, when the tracks data was laoded
  441.         $player.bind('onTrackDataLoaded.scPlayer', function(event) {
  442.           // log('onTrackDataLoaded.scPlayer', event.playerObj, playerId, event.target);
  443.           var tracks = event.playerObj.tracks;
  444.           $.each(tracks, function(index, track) {
  445.             var active = index === 0;
  446.             // create an item in the playlist
  447.             $('<li><a href="' + track.permalink_url +'">' + track.title + '</a><span class="sc-track-duration">' + timecode(track.duration) + '</span></li>').data('sc-track', {id:index}).toggleClass('active', active).appendTo($list);
  448.             // create an item in the artwork list
  449.             $('<li></li>')
  450.               .append(artworkImage(track, index >= opts.loadArtworks))
  451.               .appendTo($artworks)
  452.               .toggleClass('active', active)
  453.               .data('sc-track', track);
  454.           });
  455.           $player
  456.             .removeClass('loading')
  457.             .trigger('onPlayerInit.scPlayer');
  458.          
  459.           // update the element before rendering it in the DOM
  460.           $player.each(function() {
  461.             if($.isFunction(opts.beforeRender)){
  462.               opts.beforeRender.call(this, tracks);
  463.             }
  464.           });
  465.           // set the first track's duration
  466.           $('.sc-duration', $player)[0].innerHTML = timecode(tracks[0].duration);
  467.           $('.sc-position', $player)[0].innerHTML = timecode(0);
  468.           // set up the first track info
  469.           updateTrackInfo($player, tracks[0]);
  470.         });
  471.  
  472.  
  473.     // replace the DOM source (if there's one)
  474.     $source.each(function(index) {
  475.       $(this).replaceWith($player);
  476.     });
  477.  
  478.     return $player;
  479.   };
  480.  
  481.   // plugin wrapper
  482.   $.fn.scPlayer = function(options) {
  483.     return this.each(function() {
  484.       $.scPlayer(options, this);
  485.     });
  486.   };
  487.  
  488.   // default plugin options
  489.   $.fn.scPlayer.defaults = {
  490.     // do something with the dom object before you render it, add nodes, get more data from the services etc.
  491.     beforeRender  :   function(tracksData) {
  492.       var $player = $(this);
  493.     },
  494.     // initialization, when dom is ready
  495.     onDomReady  : function() {
  496.       $('a.sc-player, div.sc-player').scPlayer();
  497.     },
  498.     autoPlay: false,
  499.     loadArtworks: 5
  500.   };
  501.  
  502.  
  503.   // the GUI event bindings
  504.   //--------------------------------------------------------
  505.  
  506.   // toggling play/pause
  507.   $('a.sc-play, a.sc-pause').live('click', function(event) {
  508.     var $list = $(this).closest('.sc-player').find('ol.sc-trackslist');
  509.     // simulate the click in the tracklist
  510.     $list.find('li.active').click();
  511.     return false;
  512.   });
  513.  
  514.   // displaying the info panel in the player
  515.   $('a.sc-info-toggle, a.sc-info-close').live('click', function(event) {
  516.     var $link = $(this);
  517.     $link.closest('.sc-player')
  518.       .find('.sc-info').toggleClass('active').end()
  519.       .find('a.sc-info-toggle').toggleClass('active');
  520.     return false;
  521.   });
  522.  
  523.   // selecting tracks in the playlist
  524.   $('.sc-trackslist li').live('click', function(event) {
  525.     var $track = $(this),
  526.         $player = $track.closest('.sc-player'),
  527.         trackId = $track.data('sc-track').id,
  528.         play = $player.is(':not(.playing)') || $track.is(':not(.active)');
  529.     if (play) {
  530.       onPlay($player, trackId);
  531.     }else{
  532.       onPause($player);
  533.     }
  534.     $track.addClass('active').siblings('li').removeClass('active');
  535.     $('.artworks li', $player).each(function(index) {
  536.       $(this).toggleClass('active', index === trackId);
  537.     });
  538.     return false;
  539.   });
  540.  
  541.   var scrub = function(node, xPos) {
  542.     var $scrubber = $(node).closest('.sc-time-span'),
  543.         $buffer = $scrubber.find('.sc-buffer'),
  544.         $available = $scrubber.find('.sc-waveform-container img'),
  545.         $player = $scrubber.closest('.sc-player'),
  546.         relative = Math.min($buffer.width(), (xPos  - $available.offset().left)) / $available.width();
  547.     onSeek($player, relative);
  548.   };
  549.  
  550.   var onTouchMove = function(ev) {
  551.     if (ev.targetTouches.length === 1) {
  552.       scrub(ev.target, ev.targetTouches && ev.targetTouches.length && ev.targetTouches[0].clientX);
  553.       ev.preventDefault();
  554.     }
  555.   };
  556.  
  557.  
  558.   // seeking in the loaded track buffer
  559.   $('.sc-time-span')
  560.     .live('click', function(event) {
  561.       scrub(this, event.pageX);
  562.       return false;
  563.     })
  564.     .live('touchstart', function(event) {
  565.       this.addEventListener('touchmove', onTouchMove, false);
  566.       event.originalEvent.preventDefault();
  567.     })
  568.     .live('touchend', function(event) {
  569.       this.removeEventListener('touchmove', onTouchMove, false);
  570.       event.originalEvent.preventDefault();
  571.     });
  572.  
  573.   var startVolumeTracking = function(node, startEvent) {
  574.     var $node = $(node),
  575.         originX = $node.offset().left,
  576.         originWidth = $node.width(),
  577.         getVolume = function() {
  578.           return ((event.pageX - originX)/originWidth)*100;
  579.         },
  580.         update = function(event) {
  581.           $(document).trigger({type: 'scPlayer:onVolumeChange', volume: getVolume(event.pageX)});
  582.         };
  583.     $node.bind('mousemove', update);
  584.     update(startEvent);
  585.   };
  586.  
  587.   var stopVolumeTracking = function(node, event) {
  588.     $(node).unbind('mousemove');
  589.   };
  590.  
  591.   // changing volume in the player
  592.   $('.sc-volume-slider')
  593.     .live('mousedown', function(event) {
  594.       startVolumeTracking(this, event);
  595.     })
  596.     .live('mouseup', function(event) {
  597.       stopVolumeTracking(this, event);
  598.     });
  599.  
  600.   $(document).bind('scPlayer:onVolumeChange', function(event) {
  601.     $('span.sc-volume-status').css({width: event.volume + '%'});
  602.   });
  603.   // -------------------------------------------------------------------
  604.   // the default Auto-Initialization
  605.   $(function() {
  606.     if($.isFunction($.fn.scPlayer.defaults.onDomReady)){
  607.       $.fn.scPlayer.defaults.onDomReady();
  608.     }
  609.   });
  610.  
  611. })(jQuery);