full-calendar-functions.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /**
  2. *
  3. * Full Calendar callback methods.
  4. *
  5. */
  6. function scheduleRefetchEvents(json) {
  7. if(json.show_error == true){
  8. alert($.i18n._("The show instance doesn't exist anymore!"));
  9. }
  10. if(json.show_id) {
  11. var dialog_id = parseInt($("#add_show_id").val(), 10);
  12. //if you've deleted the show you are currently editing, close the add show dialog.
  13. if (dialog_id === json.show_id) {
  14. $("#add-show-close").click();
  15. }
  16. }
  17. $("#schedule_calendar").fullCalendar( 'refetchEvents' );
  18. }
  19. function makeTimeStamp(date){
  20. var sy, sm, sd, h, m, s, timestamp;
  21. sy = date.getFullYear();
  22. sm = date.getMonth() + 1;
  23. sd = date.getDate();
  24. h = date.getHours();
  25. m = date.getMinutes();
  26. s = date.getSeconds();
  27. timestamp = sy+"-"+ pad(sm, 2) +"-"+ pad(sd, 2) +" "+ pad(h, 2) +":"+ pad(m, 2) +":"+ pad(s, 2);
  28. return timestamp;
  29. }
  30. function dayClick(date, allDay, jsEvent, view){
  31. // The show from will be preloaded if the user is admin or program manager.
  32. // Hence, if the user if DJ then it won't open anything.
  33. if(userType == "A" || userType == "P"){
  34. var now, today, selected, chosenDate, chosenTime;
  35. now = adjustDateToServerDate(new Date(), serverTimezoneOffset);
  36. if(view.name === "month") {
  37. today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  38. selected = new Date(date.getFullYear(), date.getMonth(), date.getDate());
  39. }
  40. else {
  41. today = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes());
  42. selected = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes());
  43. }
  44. if(selected >= today) {
  45. var addShow = $('.add-button');
  46. //remove the +show button if it exists.
  47. if(addShow.length == 1){
  48. var span = $(addShow).parent();
  49. $(span).prev().remove();
  50. $(span).remove();
  51. }
  52. // get current duration value on the form
  53. var duration_string = $.trim($("#add_show_duration").val());
  54. var duration_info = duration_string.split(" ");
  55. var duration_h = 0;
  56. var duration_m = 0;
  57. if(duration_info[0] != null){
  58. duration_h = parseInt(duration_info[0], 10);
  59. }
  60. if(duration_info[1] != null){
  61. duration_m = parseInt(duration_info[1], 10);
  62. }
  63. // duration in milisec
  64. var duration = (duration_h * 60 * 60 * 1000) + (duration_m * 60 * 1000);
  65. var startTime_string;
  66. var startTime = 0;
  67. // get start time value on the form
  68. if(view.name === "month") {
  69. startTime_string = $("#add_show_start_time").val();
  70. var startTime_info = startTime_string.split(':');
  71. if (startTime_info.length == 2) {
  72. var start_time_temp = (parseInt(startTime_info[0],10) * 60 * 60 * 1000)
  73. + (parseInt(startTime_info[1], 10) * 60 * 1000);
  74. if (!isNaN(start_time_temp)) {
  75. startTime = start_time_temp;
  76. }
  77. }
  78. }else{
  79. // if in day or week view, selected has all the time info as well
  80. // so we don't ahve to calculate it explicitly
  81. startTime_string = pad(selected.getHours(),2)+":"+pad(selected.getMinutes(),2)
  82. startTime = 0
  83. }
  84. // calculate endDateTime
  85. var endDateTime = new Date(selected.getTime() + startTime + duration);
  86. chosenDate = selected.getFullYear() + '-' + pad(selected.getMonth()+1,2) + '-' + pad(selected.getDate(),2);
  87. var endDateFormat = endDateTime.getFullYear() + '-' + pad(endDateTime.getMonth()+1,2) + '-' + pad(endDateTime.getDate(),2);
  88. $("#add_show_start_date").val(chosenDate);
  89. $("#add_show_end_date_no_repeat").val(endDateFormat);
  90. $("#add_show_end_date").val(endDateFormat);
  91. if(view.name !== "month") {
  92. var endTimeString = pad(endDateTime.getHours(),2)+":"+pad(endDateTime.getMinutes(),2);
  93. $("#add_show_start_time").val(startTime_string)
  94. $("#add_show_end_time").val(endTimeString)
  95. }
  96. $("#schedule-show-when").show();
  97. openAddShowForm();
  98. }
  99. }
  100. }
  101. function viewDisplay( view ) {
  102. view_name = view.name;
  103. if(view.name === 'agendaDay' || view.name === 'agendaWeek') {
  104. var calendarEl = this;
  105. var select = $('<select class="schedule_change_slots input_select"/>')
  106. .append('<option value="1">'+$.i18n._("1m")+'</option>')
  107. .append('<option value="5">'+$.i18n._("5m")+'</option>')
  108. .append('<option value="10">'+$.i18n._("10m")+'</option>')
  109. .append('<option value="15">'+$.i18n._("15m")+'</option>')
  110. .append('<option value="30">'+$.i18n._("30m")+'</option>')
  111. .append('<option value="60">'+$.i18n._("60m")+'</option>')
  112. .change(function(){
  113. var slotMin = $(this).val();
  114. var opt = view.calendar.options;
  115. var date = $(calendarEl).fullCalendar('getDate');
  116. opt.slotMinutes = parseInt(slotMin);
  117. opt.events = getFullCalendarEvents;
  118. opt.defaultView = view.name;
  119. //re-initialize calendar with new slotmin options
  120. $(calendarEl)
  121. .fullCalendar('destroy')
  122. .fullCalendar(opt)
  123. .fullCalendar( 'gotoDate', date );
  124. //save slotMin value to db
  125. var url = baseUrl+'Schedule/set-time-interval/format/json';
  126. $.post(url, {timeInterval: slotMin});
  127. });
  128. var topLeft = $(view.element).find("table.fc-agenda-days > thead th:first");
  129. //select.width(topLeft.width())
  130. // .height(topLeft.height());
  131. topLeft.empty()
  132. .append(select);
  133. var slotMin = view.calendar.options.slotMinutes;
  134. $('.schedule_change_slots option[value="'+slotMin+'"]').attr('selected', 'selected');
  135. }
  136. if(($("#add-show-form").length == 1) && ($("#add-show-form").css('display')=='none') && ($('.fc-header-left > span').length == 5)) {
  137. //userType is defined in bootstrap.php, and is derived from the currently logged in user.
  138. if(userType == "A" || userType == "P"){
  139. makeAddShowButton();
  140. }
  141. }
  142. //save view name to db
  143. var url = baseUrl+'Schedule/set-time-scale/format/json';
  144. $.post(url, {timeScale: view.name});
  145. }
  146. function eventRender(event, element, view) {
  147. $(element).addClass("fc-show-instance-"+event.id);
  148. $(element).attr("data-show-id", event.showId);
  149. $(element).attr("data-show-linked", event.linked);
  150. $(element).data("event", event);
  151. //only put progress bar on shows that aren't being recorded.
  152. if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 0) {
  153. var div = $('<div/>');
  154. div
  155. .height('5px')
  156. .width('95%')
  157. .css('margin-top', '1px')
  158. .css('margin-left', 'auto')
  159. .css('margin-right', 'auto')
  160. .progressbar({
  161. value: event.percent
  162. });
  163. $(element).find(".fc-event-content").append(div);
  164. }
  165. //add the record/rebroadcast/soundcloud icons if needed
  166. if (event.record === 1) {
  167. if (view.name === 'agendaDay' || view.name === 'agendaWeek') {
  168. if (event.soundcloud_id === -1) {
  169. $(element).find(".fc-event-time").before('<span class="small-icon recording"></span>');
  170. } else if ( event.soundcloud_id > 0) {
  171. $(element).find(".fc-event-time").before('<span class="small-icon recording"></span><span class="small-icon soundcloud"></span>');
  172. } else if (event.soundcloud_id === -2) {
  173. $(element).find(".fc-event-time").before('<span class="small-icon recording"></span><span class="small-icon progress"></span>');
  174. } else if (event.soundcloud_id === -3) {
  175. $(element).find(".fc-event-time").before('<span class="small-icon recording"></span><span class="small-icon sc-error"></span>');
  176. }
  177. } else if (view.name === 'month') {
  178. if(event.soundcloud_id === -1) {
  179. $(element).find(".fc-event-title").after('<span class="small-icon recording"></span>');
  180. } else if (event.soundcloud_id > 0) {
  181. $(element).find(".fc-event-title").after('<span class="small-icon recording"></span><span class="small-icon soundcloud"></span>');
  182. } else if (event.soundcloud_id === -2) {
  183. $(element).find(".fc-event-title").after('<span class="small-icon recording"></span><span class="small-icon progress"></span>');
  184. } else if (event.soundcloud_id === -3) {
  185. $(element).find(".fc-event-title").after('<span class="small-icon recording"></span><span class="small-icon sc-error"></span>');
  186. }
  187. }
  188. }
  189. if (event.record === 0 && event.rebroadcast === 0) {
  190. if (view.name === 'agendaDay' || view.name === 'agendaWeek') {
  191. if (event.show_empty === 1) {
  192. if (event.linked) {
  193. $(element)
  194. .find(".fc-event-time")
  195. .before('<span class="small-icon linked"></span><span class="small-icon show-empty"></span>');
  196. } else {
  197. $(element)
  198. .find(".fc-event-time")
  199. .before('<span class="small-icon show-empty"></span>');
  200. }
  201. } else if (event.show_partial_filled === true) {
  202. if (event.linked) {
  203. $(element)
  204. .find(".fc-event-time")
  205. .before('<span class="small-icon linked"></span><span class="small-icon show-partial-filled"></span>');
  206. } else {
  207. $(element)
  208. .find(".fc-event-time")
  209. .before('<span class="small-icon show-partial-filled"></span>');
  210. }
  211. } else {
  212. if (event.linked) {
  213. $(element)
  214. .find(".fc-event-time")
  215. .before('<span class="small-icon linked"></span>');
  216. }
  217. }
  218. } else if (view.name === 'month') {
  219. if (event.show_empty === 1) {
  220. if (event.linked) {
  221. $(element)
  222. .find(".fc-event-title")
  223. .after('<span class="small-icon linked"></span><span title="'+$.i18n._("Show is empty")+'" class="small-icon show-empty"></span>');
  224. } else {
  225. $(element)
  226. .find(".fc-event-title")
  227. .after('<span title="'+$.i18n._("Show is empty")+'" class="small-icon show-empty"></span>');
  228. }
  229. } else if (event.show_partial_filled === true) {
  230. if (event.linked) {
  231. $(element)
  232. .find(".fc-event-title")
  233. .after('<span class="small-icon linked"></span><span title="'+$.i18n._("Show is partially filled")+'" class="small-icon show-partial-filled"></span>');
  234. } else {
  235. $(element)
  236. .find(".fc-event-title")
  237. .after('<span title="'+$.i18n._("Show is partially filled")+'" class="small-icon show-partial-filled"></span>');
  238. }
  239. } else {
  240. if (event.linked) {
  241. $(element)
  242. .find(".fc-event-title")
  243. .after('<span class="small-icon linked"></span>');
  244. }
  245. }
  246. }
  247. }
  248. //rebroadcast icon
  249. if (event.rebroadcast === 1) {
  250. if (view.name === 'agendaDay' || view.name === 'agendaWeek') {
  251. $(element).find(".fc-event-time").before('<span class="small-icon rebroadcast"></span>');
  252. } else if (view.name === 'month') {
  253. $(element).find(".fc-event-title").after('<span class="small-icon rebroadcast"></span>');
  254. }
  255. }
  256. //now playing icon.
  257. var span = '<span class="small-icon now-playing"></span>';
  258. if (event.nowPlaying === true) {
  259. if (view_name === 'agendaDay' || view_name === 'agendaWeek') {
  260. $(element).find(".fc-event-time").before(span);
  261. }
  262. else if (view_name === 'month') {
  263. $(element).find(".fc-event-title").after(span);
  264. }
  265. }
  266. }
  267. function eventAfterRender( event, element, view ) {
  268. $(element).find(".small-icon").live('mouseover',function(){
  269. addQtipsToIcons($(this), event.id);
  270. });
  271. }
  272. function eventDrop(event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
  273. var url = baseUrl+'Schedule/move-show/format/json';
  274. $.post(url,
  275. {day: dayDelta, min: minuteDelta, showInstanceId: event.id},
  276. function(json){
  277. if(json.show_error == true){
  278. alertShowErrorAndReload();
  279. }
  280. if(json.error) {
  281. alert(json.error);
  282. revertFunc();
  283. }
  284. //Workaround for cases where FullCalendar handles events over DST
  285. //time changes in a different way than Airtime does.
  286. //(Airtime preserves show duration, FullCalendar doesn't.)
  287. scheduleRefetchEvents(json);
  288. });
  289. }
  290. function eventResize( event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view ) {
  291. var url = baseUrl+'Schedule/resize-show/format/json';
  292. $.post(url,
  293. {day: dayDelta, min: minuteDelta, showId: event.showId, instanceId: event.id},
  294. function(json){
  295. if(json.show_error == true){
  296. alertShowErrorAndReload();
  297. }
  298. if(json.error) {
  299. alert(json.error);
  300. revertFunc();
  301. }
  302. scheduleRefetchEvents(json);
  303. });
  304. }
  305. function windowResize() {
  306. // 200 px for top dashboard and 50 for padding on main content
  307. // this calculation was copied from schedule.js line 326
  308. var mainHeight = $(window).height() - 200 - 24;
  309. $('#schedule_calendar').fullCalendar('option', 'contentHeight', mainHeight);
  310. }
  311. function preloadEventFeed () {
  312. var url = baseUrl+'Schedule/event-feed-preload';
  313. var d = new Date();
  314. $.post(url, {format: "json", cachep: d.getTime()}, function(json){
  315. calendarEvents = json.events;
  316. createFullCalendar({calendarInit: calendarPref});
  317. });
  318. }
  319. var initialLoad = true;
  320. function getFullCalendarEvents(start, end, callback) {
  321. if (initialLoad) {
  322. initialLoad = false;
  323. callback(calendarEvents);
  324. } else {
  325. var url, start_date, end_date;
  326. start_date = makeTimeStamp(start);
  327. end_date = makeTimeStamp(end);
  328. url = baseUrl+'Schedule/event-feed';
  329. var d = new Date();
  330. $.post(url, {format: "json", start: start_date, end: end_date, cachep: d.getTime()}, function(json){
  331. callback(json.events);
  332. });
  333. }
  334. }
  335. function checkSCUploadStatus(){
  336. var url = baseUrl+'Library/get-upload-to-soundcloud-status/format/json',
  337. id;
  338. $("span[class*=progress]").each(function(){
  339. id = $(this).parents("div.fc-event").data("event").id;
  340. $.post(url, {format: "json", id: id, type:"show"}, function(json){
  341. if (json.sc_id > 0){
  342. $(".fc-show-instance-"+id)
  343. .find(".progress")
  344. .removeClass("progress")
  345. .addClass("soundcloud");
  346. }
  347. else if (json.sc_id == "-3"){
  348. $(".fc-show-instance-"+id)
  349. .find(".progress")
  350. .removeClass("progress")
  351. .addClass("sc-error");
  352. }
  353. setTimeout(checkSCUploadStatus, 5000);
  354. });
  355. });
  356. }
  357. /** This function adds and removes the current
  358. * show icon
  359. */
  360. function getCurrentShow() {
  361. var url = baseUrl+'Schedule/get-current-show/format/json';
  362. function addNowPlaying(json) {
  363. var $el,
  364. span = '<span class="small-icon now-playing"></span>';
  365. $(".now-playing").remove();
  366. if (json.current_show === true) {
  367. $el = $(".fc-show-instance-"+json.si_id);
  368. if (view_name === 'agendaDay' || view_name === 'agendaWeek') {
  369. $el.find(".fc-event-time").before(span);
  370. }
  371. else if (view_name === 'month') {
  372. $el.find(".fc-event-title").after(span);
  373. }
  374. }
  375. setTimeout(getCurrentShow, 5000);
  376. }
  377. $.post(url, {format: "json"}, addNowPlaying);
  378. }
  379. function addQtipsToIcons(ele, id){
  380. if ($(ele).hasClass("progress")){
  381. $(ele).qtip({
  382. content: {
  383. text: $.i18n._("Uploading in progress...")
  384. },
  385. position:{
  386. adjust: {
  387. resize: true,
  388. method: "flip flip"
  389. },
  390. at: "right center",
  391. my: "left top",
  392. viewport: $(window)
  393. },
  394. style: {
  395. classes: "ui-tooltip-dark file-md-long"
  396. },
  397. show: {
  398. ready: true // Needed to make it show on first mouseover event
  399. }
  400. });
  401. }else if($(ele).hasClass("soundcloud")){
  402. $(ele).qtip({
  403. content: {
  404. text: $.i18n._("Retreiving data from the server..."),
  405. ajax: {
  406. url: baseUrl+"Library/get-upload-to-soundcloud-status",
  407. type: "post",
  408. data: ({format: "json", id : id, type: "show"}),
  409. success: function(json, status){
  410. this.set('content.text', $.i18n._("The soundcloud id for this file is: ")+json.sc_id);
  411. }
  412. }
  413. },
  414. position:{
  415. adjust: {
  416. resize: true,
  417. method: "flip flip"
  418. },
  419. at: "right center",
  420. my: "left top",
  421. viewport: $(window)
  422. },
  423. style: {
  424. classes: "ui-tooltip-dark file-md-long"
  425. },
  426. show: {
  427. ready: true // Needed to make it show on first mouseover event
  428. }
  429. });
  430. }else if($(ele).hasClass("sc-error")){
  431. $(ele).qtip({
  432. content: {
  433. text: $.i18n._("Retreiving data from the server..."),
  434. ajax: {
  435. url: baseUrl+"Library/get-upload-to-soundcloud-status",
  436. type: "post",
  437. data: ({format: "json", id : id, type: "show"}),
  438. success: function(json, status){
  439. this.set('content.text', $.i18n._("There was error while uploading to soundcloud.")+"<br>"+$.i18n._("Error code: ")+json.error_code+
  440. "<br>"+$.i18n._("Error msg: ")+json.error_msg+"<br>");
  441. }
  442. }
  443. },
  444. position:{
  445. adjust: {
  446. resize: true,
  447. method: "flip flip"
  448. },
  449. at: "right center",
  450. my: "left top",
  451. viewport: $(window)
  452. },
  453. style: {
  454. classes: "ui-tooltip-dark file-md-long"
  455. },
  456. show: {
  457. ready: true // Needed to make it show on first mouseover event
  458. }
  459. });
  460. }else if ($(ele).hasClass("show-empty")){
  461. $(ele).qtip({
  462. content: {
  463. text: $.i18n._("This show has no scheduled content.")
  464. },
  465. position:{
  466. adjust: {
  467. resize: true,
  468. method: "flip flip"
  469. },
  470. at: "right center",
  471. my: "left top",
  472. viewport: $(window)
  473. },
  474. style: {
  475. classes: "ui-tooltip-dark file-md-long"
  476. },
  477. show: {
  478. ready: true // Needed to make it show on first mouseover event
  479. }
  480. });
  481. } else if ($(ele).hasClass("show-partial-filled")){
  482. $(ele).qtip({
  483. content: {
  484. text: $.i18n._("This show is not completely filled with content.")
  485. },
  486. position:{
  487. adjust: {
  488. resize: true,
  489. method: "flip flip"
  490. },
  491. at: "right center",
  492. my: "left top",
  493. viewport: $(window)
  494. },
  495. style: {
  496. classes: "ui-tooltip-dark file-md-long"
  497. },
  498. show: {
  499. ready: true // Needed to make it show on first mouseover event
  500. }
  501. });
  502. }
  503. }
  504. //Alert the error and reload the page
  505. //this function is used to resolve concurrency issue
  506. function alertShowErrorAndReload(){
  507. alert($.i18n._("The show instance doesn't exist anymore!"));
  508. window.location.reload();
  509. }
  510. $(document).ready(function(){
  511. preloadEventFeed();
  512. checkSCUploadStatus();
  513. getCurrentShow();
  514. });
  515. var view_name;