dashboard.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. //approximate server time, because once we receive it from the server,
  2. //there way have been a great amount of latency and it is no longer accurate.
  3. var approximateServerTime = null;
  4. var localRemoteTimeOffset = null;
  5. var previousSong = null;
  6. var currentSong = null;
  7. var nextSong = null;
  8. var currentShow = new Array();
  9. var nextShow = new Array();
  10. var showName = null;
  11. var currentElem;
  12. var serverUpdateInterval = 5000;
  13. var uiUpdateInterval = 200;
  14. var master_dj_on_air = false;
  15. var live_dj_on_air = false;
  16. var scheduled_play_on_air = false;
  17. var scheduled_play_source = false;
  18. //a reference returned by setTimeout. Useful for when we want clearTimeout()
  19. var newSongTimeoutId = null;
  20. //a reference returned by setTimeout. Useful for when we want clearTimeout()
  21. var newShowTimeoutId = null;
  22. //keep track of how many UI refreshes the ON-AIR light has been off for.
  23. //For example, the uiUpdateInterval is every 200ms, so if onAirOffIterations
  24. //is 25, then that means 5 seconds have gone by.
  25. var onAirOffIterations = 0;
  26. /* boolean flag to let us know if we should prepare to execute a function
  27. * that flips the playlist to the next song. This flag's purpose is to
  28. * make sure the function is only executed once*/
  29. var nextSongPrepare = true;
  30. var nextShowPrepare = true;
  31. function secondsTimer(){
  32. /* This function constantly calls itself every 'uiUpdateInterval'
  33. * micro-seconds and is responsible for updating the UI. */
  34. if (localRemoteTimeOffset !== null){
  35. var date = new Date();
  36. approximateServerTime = date.getTime() - localRemoteTimeOffset;
  37. updateProgressBarValue();
  38. updatePlaybar();
  39. controlOnAirLight();
  40. controlSwitchLight();
  41. }
  42. setTimeout(secondsTimer, uiUpdateInterval);
  43. }
  44. function newSongStart(){
  45. nextSongPrepare = true;
  46. if (nextSong.type == 'track') {
  47. currentSong = nextSong;
  48. nextSong = null;
  49. }
  50. }
  51. function nextShowStart(){
  52. nextShowPrepare = true;
  53. currentShow[0] = nextShow.shift();
  54. }
  55. /* Called every "uiUpdateInterval" mseconds. */
  56. function updateProgressBarValue(){
  57. var showPercentDone = 0;
  58. if (currentShow.length > 0){
  59. showPercentDone = (approximateServerTime - currentShow[0].showStartPosixTime)/currentShow[0].showLengthMs*100;
  60. if (showPercentDone < 0 || showPercentDone > 100){
  61. showPercentDone = 0;
  62. currentShow = new Array();
  63. currentSong = null;
  64. }
  65. }
  66. $('#progress-show').attr("style", "width:"+showPercentDone+"%");
  67. var songPercentDone = 0;
  68. var scheduled_play_div = $("#scheduled_play_div");
  69. var scheduled_play_line_to_switch = scheduled_play_div.parent().find(".line-to-switch");
  70. if (currentSong !== null){
  71. var songElapsedTime = 0;
  72. songPercentDone = (approximateServerTime - currentSong.songStartPosixTime)/currentSong.songLengthMs*100;
  73. songElapsedTime = approximateServerTime - currentSong.songStartPosixTime;
  74. if (songPercentDone < 0) {
  75. songPercentDone = 0;
  76. //currentSong = null;
  77. } else if (songPercentDone > 100) {
  78. songPercentDone = 100;
  79. } else {
  80. if ((currentSong.media_item_played == true && currentShow.length > 0) || (songElapsedTime < 5000 && currentShow[0].record != 1)) {
  81. scheduled_play_line_to_switch.attr("class", "line-to-switch on");
  82. scheduled_play_div.addClass("ready");
  83. scheduled_play_source = true;
  84. }
  85. else{
  86. scheduled_play_source = false;
  87. scheduled_play_line_to_switch.attr("class", "line-to-switch off");
  88. scheduled_play_div.removeClass("ready");
  89. }
  90. $('#progress-show').attr("class", "progress-show");
  91. }
  92. } else {
  93. scheduled_play_source = false;
  94. scheduled_play_line_to_switch.attr("class", "line-to-switch off");
  95. scheduled_play_div.removeClass("ready");
  96. $('#progress-show').attr("class", "progress-show-error");
  97. }
  98. $('#progress-bar').attr("style", "width:"+songPercentDone+"%");
  99. }
  100. function updatePlaybar(){
  101. /* Column 0 update */
  102. if (previousSong !== null){
  103. $('#previous').text(previousSong.name+",");
  104. $('#prev-length').text(convertToHHMMSSmm(previousSong.songLengthMs));
  105. }else{
  106. $('#previous').empty();
  107. $('#prev-length').empty();
  108. }
  109. if (currentSong !== null && !master_dj_on_air && !live_dj_on_air){
  110. if (currentSong.record == "1")
  111. $('#current').html("<span style='color:red; font-weight:bold'>"+$.i18n._("Recording:")+"</span>"+currentSong.name+",");
  112. else
  113. $('#current').text(currentSong.name+",");
  114. }else{
  115. if (master_dj_on_air) {
  116. if (showName) {
  117. $('#current').html($.i18n._("Current")+": <span style='color:red; font-weight:bold'>"+showName+" - "+$.i18n._("Master Stream")+"</span>");
  118. } else {
  119. $('#current').html($.i18n._("Current")+": <span style='color:red; font-weight:bold'>"+$.i18n._("Master Stream")+"</span>");
  120. }
  121. } else if (live_dj_on_air) {
  122. if (showName) {
  123. $('#current').html($.i18n._("Current")+": <span style='color:red; font-weight:bold'>"+showName+" - "+$.i18n._("Live Stream")+"</span>");
  124. } else {
  125. $('#current').html($.i18n._("Current")+": <span style='color:red; font-weight:bold'>"+$.i18n._("Live Stream")+"</span>");
  126. }
  127. } else {
  128. $('#current').html($.i18n._("Current")+": <span style='color:red; font-weight:bold'>"+$.i18n._("Nothing Scheduled")+"</span>");
  129. }
  130. }
  131. if (nextSong !== null){
  132. $('#next').text(nextSong.name+",");
  133. $('#next-length').text(convertToHHMMSSmm(nextSong.songLengthMs));
  134. }else{
  135. $('#next').empty();
  136. $('#next-length').empty();
  137. }
  138. $('#start').empty();
  139. $('#end').empty();
  140. $('#time-elapsed').empty();
  141. $('#time-remaining').empty();
  142. $('#song-length').empty();
  143. if (currentSong !== null && !master_dj_on_air && !live_dj_on_air){
  144. $('#start').text(currentSong.starts.split(' ')[1]);
  145. $('#end').text(currentSong.ends.split(' ')[1]);
  146. /* Get rid of the millisecond accuracy so that the second counters for both
  147. * show and song change at the same time. */
  148. var songStartRoughly = parseInt(Math.round(currentSong.songStartPosixTime/1000), 10)*1000;
  149. var songEndRoughly = parseInt(Math.round(currentSong.songEndPosixTime/1000), 10)*1000;
  150. $('#time-elapsed').text(convertToHHMMSS(approximateServerTime - songStartRoughly));
  151. $('#time-remaining').text(convertToHHMMSS(songEndRoughly - approximateServerTime));
  152. $('#song-length').text(convertToHHMMSS(currentSong.songLengthMs));
  153. }
  154. /* Column 1 update */
  155. $('#playlist').text($.i18n._("Current Show:"));
  156. var recElem = $('.recording-show');
  157. if (currentShow.length > 0){
  158. $('#playlist').text(currentShow[0].name);
  159. (currentShow[0].record == "1") ? recElem.show(): recElem.hide();
  160. } else {
  161. recElem.hide();
  162. }
  163. $('#show-length').empty();
  164. if (currentShow.length > 0){
  165. $('#show-length').text(convertDateToHHMM(currentShow[0].showStartPosixTime) + " - " + convertDateToHHMM(currentShow[0].showEndPosixTime));
  166. }
  167. /* Column 2 update */
  168. $('#time').text(convertDateToHHMMSS(approximateServerTime));
  169. }
  170. function calcAdditionalData(currentItem){
  171. currentItem.songStartPosixTime = convertDateToPosixTime(currentItem.starts);
  172. currentItem.songEndPosixTime = convertDateToPosixTime(currentItem.ends);
  173. currentItem.songLengthMs = currentItem.songEndPosixTime - currentItem.songStartPosixTime;
  174. }
  175. function calcAdditionalShowData(show){
  176. if (show.length > 0){
  177. show[0].showStartPosixTime = convertDateToPosixTime(show[0].start_timestamp);
  178. show[0].showEndPosixTime = convertDateToPosixTime(show[0].end_timestamp);
  179. show[0].showLengthMs = show[0].showEndPosixTime - show[0].showStartPosixTime;
  180. }
  181. }
  182. function calculateTimeToNextSong() {
  183. if (approximateServerTime === null) {
  184. return;
  185. }
  186. if (newSongTimeoutId !== null) {
  187. /* We have a previous timeout set, let's unset it */
  188. clearTimeout(newSongTimeoutId);
  189. newSongTimeoutId = null;
  190. }
  191. var diff = nextSong.songStartPosixTime - approximateServerTime;
  192. if (diff < 0) diff=0;
  193. nextSongPrepare = false;
  194. newSongTimeoutId= setTimeout(newSongStart, diff);
  195. }
  196. function calculateTimeToNextShow() {
  197. if (approximateServerTime === null) {
  198. return;
  199. }
  200. if (newShowTimeoutId !== null) {
  201. /* We have a previous timeout set, let's unset it */
  202. clearTimeout(newShowTimeoutId);
  203. newShowTimeoutId = null;
  204. }
  205. var diff = nextShow[0].showStartPosixTime - approximateServerTime;
  206. if (diff < 0) diff=0;
  207. nextShowPrepare = false;
  208. newShowTimeoutId= setTimeout(nextShowStart, diff);
  209. }
  210. function parseItems(obj){
  211. $('#time-zone').text(obj.timezone);
  212. previousSong = obj.previous;
  213. currentSong = obj.current;
  214. nextSong = obj.next;
  215. if (previousSong !== null) {
  216. calcAdditionalData(previousSong);
  217. }
  218. if (currentSong !== null) {
  219. calcAdditionalData(currentSong);
  220. }
  221. if (nextSong !== null) {
  222. calcAdditionalData(nextSong);
  223. calculateTimeToNextSong();
  224. }
  225. currentShow = new Array();
  226. if (obj.currentShow.length > 0) {
  227. calcAdditionalShowData(obj.currentShow);
  228. currentShow = obj.currentShow;
  229. }
  230. nextShow = new Array();
  231. if (obj.nextShow.length > 0) {
  232. calcAdditionalShowData(obj.nextShow);
  233. nextShow = obj.nextShow;
  234. calculateTimeToNextShow();
  235. }
  236. var schedulePosixTime = convertDateToPosixTime(obj.schedulerTime);
  237. var date = new Date();
  238. localRemoteTimeOffset = date.getTime() - schedulePosixTime;
  239. }
  240. function parseSourceStatus(obj){
  241. var live_div = $("#live_dj_div");
  242. var master_div = $("#master_dj_div");
  243. var live_li = live_div.parent();
  244. var master_li = master_div.parent();
  245. if(obj.live_dj_source == false){
  246. live_li.find(".line-to-switch").attr("class", "line-to-switch off");
  247. live_div.removeClass("ready");
  248. }else{
  249. live_li.find(".line-to-switch").attr("class", "line-to-switch on");
  250. live_div.addClass("ready");
  251. }
  252. if(obj.master_dj_source == false){
  253. master_li.find(".line-to-switch").attr("class", "line-to-switch off");
  254. master_div.removeClass("ready");
  255. }else{
  256. master_li.find(".line-to-switch").attr("class", "line-to-switch on");
  257. master_div.addClass("ready");
  258. }
  259. }
  260. function parseSwitchStatus(obj){
  261. if(obj.live_dj_source == "on"){
  262. live_dj_on_air = true;
  263. }else{
  264. live_dj_on_air = false;
  265. }
  266. if(obj.master_dj_source == "on"){
  267. master_dj_on_air = true;
  268. }else{
  269. master_dj_on_air = false;
  270. }
  271. if(obj.scheduled_play == "on"){
  272. scheduled_play_on_air = true;
  273. }else{
  274. scheduled_play_on_air = false;
  275. }
  276. var scheduled_play_switch = $("#scheduled_play.source-switch-button");
  277. var live_dj_switch = $("#live_dj.source-switch-button");
  278. var master_dj_switch = $("#master_dj.source-switch-button");
  279. scheduled_play_switch.find("span").html(obj.scheduled_play);
  280. if(scheduled_play_on_air){
  281. scheduled_play_switch.addClass("active");
  282. }else{
  283. scheduled_play_switch.removeClass("active");
  284. }
  285. live_dj_switch.find("span").html(obj.live_dj_source);
  286. if(live_dj_on_air){
  287. live_dj_switch.addClass("active");
  288. }else{
  289. live_dj_switch.removeClass("active");
  290. }
  291. master_dj_switch.find("span").html(obj.master_dj_source)
  292. if(master_dj_on_air){
  293. master_dj_switch.addClass("active");
  294. }else{
  295. master_dj_switch.removeClass("active");
  296. }
  297. }
  298. function controlOnAirLight(){
  299. if ((scheduled_play_on_air && scheduled_play_source) || live_dj_on_air || master_dj_on_air) {
  300. $('#on-air-info').attr("class", "on-air-info on");
  301. onAirOffIterations = 0;
  302. } else if (onAirOffIterations < 20) {
  303. //if less than 4 seconds have gone by (< 20 executions of this function)
  304. //then keep the ON-AIR light on. Only after at least 3 seconds have gone by,
  305. //should we be allowed to turn it off. This is to stop the light from temporarily turning
  306. //off between tracks: CC-3725
  307. onAirOffIterations++;
  308. } else {
  309. $('#on-air-info').attr("class", "on-air-info off");
  310. }
  311. }
  312. function controlSwitchLight(){
  313. var live_li= $("#live_dj_div").parent();
  314. var master_li = $("#master_dj_div").parent();
  315. var scheduled_play_li = $("#scheduled_play_div").parent();
  316. if((scheduled_play_on_air && scheduled_play_source) && !live_dj_on_air && !master_dj_on_air){
  317. scheduled_play_li.find(".line-to-on-air").attr("class", "line-to-on-air on");
  318. live_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  319. master_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  320. }else if(live_dj_on_air && !master_dj_on_air){
  321. scheduled_play_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  322. live_li.find(".line-to-on-air").attr("class", "line-to-on-air on");
  323. master_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  324. }else if(master_dj_on_air){
  325. scheduled_play_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  326. live_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  327. master_li.find(".line-to-on-air").attr("class", "line-to-on-air on");
  328. }else{
  329. scheduled_play_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  330. live_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  331. master_li.find(".line-to-on-air").attr("class", "line-to-on-air off");
  332. }
  333. }
  334. function getScheduleFromServer(){
  335. $.ajax({ url: baseUrl+"Schedule/get-current-playlist/format/json",
  336. dataType:"json",
  337. success:function(data){
  338. parseItems(data.entries);
  339. parseSourceStatus(data.source_status);
  340. parseSwitchStatus(data.switch_status);
  341. showName = data.show_name;
  342. setTimeout(getScheduleFromServer, serverUpdateInterval);
  343. }, error:function(jqXHR, textStatus, errorThrown){}});
  344. }
  345. function setupQtip(){
  346. var qtipElem = $('#about-link');
  347. if (qtipElem.length > 0){
  348. qtipElem.qtip({
  349. content: $('#about-txt').html(),
  350. show: 'mouseover',
  351. hide: { when: 'mouseout', fixed: true },
  352. position: {
  353. corner: {
  354. target: 'center',
  355. tooltip: 'topRight'
  356. }
  357. },
  358. style: {
  359. border: {
  360. width: 0,
  361. radius: 4
  362. },
  363. name: 'light' // Use the default light style
  364. }
  365. });
  366. }
  367. }
  368. function setSwitchListener(ele){
  369. var sourcename = $(ele).attr('id');
  370. var status_span = $(ele).find("span");
  371. var status = status_span.html();
  372. $.get(baseUrl+"Dashboard/switch-source/format/json/sourcename/"+sourcename+"/status/"+status, function(data){
  373. if(data.error){
  374. alert(data.error);
  375. }else{
  376. if(data.status == "ON"){
  377. $(ele).addClass("active");
  378. }else{
  379. $(ele).removeClass("active");
  380. }
  381. status_span.html(data.status);
  382. }
  383. });
  384. }
  385. function kickSource(ele){
  386. var sourcename = $(ele).attr('id');
  387. $.get(baseUrl+"Dashboard/disconnect-source/format/json/sourcename/"+sourcename, function(data){
  388. if(data.error){
  389. alert(data.error);
  390. }
  391. });
  392. }
  393. var stream_window = null;
  394. function init() {
  395. //begin producer "thread"
  396. getScheduleFromServer();
  397. //begin consumer "thread"
  398. secondsTimer();
  399. setupQtip();
  400. $('.listen-control-button').click(function() {
  401. if (stream_window == null || stream_window.closed)
  402. stream_window=window.open(baseUrl+"Dashboard/stream-player", 'name', 'width=400,height=158');
  403. stream_window.focus();
  404. return false;
  405. });
  406. }
  407. /* We never retrieve the user's password from the db
  408. * and when we call isValid($params) the form values are cleared
  409. * and repopulated with $params which does not have the password
  410. * field. Therefore, we fill the password field with 6 x's
  411. */
  412. function setCurrentUserPseudoPassword() {
  413. $('#cu_password').val("xxxxxx");
  414. $('#cu_passwordVerify').val("xxxxxx");
  415. }
  416. $(document).ready(function() {
  417. if ($('#master-panel').length > 0)
  418. init();
  419. if ($('.errors').length === 0) {
  420. setCurrentUserPseudoPassword();
  421. }
  422. $('body').on('click','#current-user', function() {
  423. $.ajax({
  424. url: baseUrl+'user/edit-user/format/json'
  425. });
  426. });
  427. $('body').on('click', '#cu_save_user', function() {
  428. $.cookie("airtime_locale", $('#cu_locale').val(), {path: '/'});
  429. });
  430. // When the 'Listen' button is clicked we set the width
  431. // of the share button to the width of the 'Live Stream'
  432. // text. This differs depending on the language setting
  433. $('#popup-link').css('width', $('.jp-container h1').css('width'));
  434. });