dnum5emepower / Flatten Zimbra Calendar Background Colors and Grey Elapsed Time and Events DEV

// ==UserScript==
// @name         Flatten Zimbra Calendar Background Colors and Grey Elapsed Time and Events DEV
// @version      1.3
// @author       You
// @match        https://partage.unistra.fr/*
// @grant        none
// @license      MIT
// ==/UserScript==

//Observer that checks if the calendar node exists
var calendarObserver = new MutationObserver(function (mutations, observer) {
  console.log("Document mutation detected");

  //If the calendar node exists
  if (document.getElementsByClassName("ZmCalViewMgr").length > 0) {
    console.log("Calendar detected");

    //regex to identify if the correct style is applied afterwards
    var re = /rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\) none repeat scroll 0% 0%/;
    //Create an events observer that will change all backgrounds when triggered
    var eventsObserver = new MutationObserver(function (mutations, observer) {
      console.log("Event mutation detected");
      changeBackgroundStyle(eventsObserver, re);
    });

    //Events observer is triggered when background attributes of calendar node or any of its children are modified
    eventsObserver.observe(document.getElementsByClassName("ZmCalViewMgr")[0], {
      subtree: true,
      attributes: true,
      attributesFilter: ['background', 'opacity'],
      childList: true
    });

    //We disconnect the calendar observer since the events observer is now up and running
    calendarObserver.disconnect();
  }
});

//Calendar observer is triggered whenever a change is made in the document
calendarObserver.observe(document, {
  subtree: true,
  attributes: true
});

//Function to change the backgrounds to a flatter, easier to read format
function changeBackgroundStyle(eventsObserver, re) {
  //Get current date, day, hours and minutes
  var currentDate = new Date();
  var currentDay = currentDate.getDay();
  var currentHour = currentDate.getHours() + ':' + currentDate.getMinutes();
  var lengthOfDay = Date.parse("01/01/1970 17:00") - Date.parse("01/01/1970 8:00");
  var advancementOfDay = ((Date.parse("01/01/1970 " + currentHour) - Date.parse("01/01/1970 8:00")) / lengthOfDay * 100).toFixed(1);
  //console.log(advancementOfDay);

  //Temporarily disconnect the events observer otherwise our background manipulations will trigger an infinite loop (not needed for firefox 66+, how come ?)
  eventsObserver.disconnect();

  //Retrieve all day grids
  var events = document.querySelectorAll('*[id$="_tableBody"]');
  var dayGrids = Array.from(document.getElementsByClassName('ImgCalendarDayGrid'));
  dayGrids.splice(0, 1);
  var dayGridDay = 0;
  //For all day grids
  dayGrids.forEach(function (dayGrid) {
    //console.log(dayGrid);
    if (dayGrid.style.width != "" && dayGrid.style.height != "") {
      dayGridDay++;
      if (dayGridDay < currentDay) {
        dayGrid.parentNode.style.background = "rgba(227,227,220,1)";
      }
      else if (dayGridDay === currentDay) {
        //console.log("linear-gradient(180deg, rgba(227,227,220,1) 0%, rgba(227,227,220,1) " + advancementOfDay + "%, rgba(255,255,255,1) " + (parseInt(advancementOfDay) + 1) + "%, rgba(255,255,255,1) 100%);")
        dayGrid.parentNode.style.background = "linear-gradient(180deg, rgba(227,227,220,1) 0%, rgba(227,227,220,1) " + advancementOfDay + "%, rgba(255,255,255,1) " + (parseInt(advancementOfDay) + 1) + "%,rgba(255,255,255,1) 100%)";
      }
    }
  });

  //For all events
  events.forEach(function (event) {
    //Delete the very small and useless border between the vertical bar and the event box itself
    event.style.border = 0;

    //If event has end date node, get its value otherwise build it based on the height of the event
    if (event.firstChild.childNodes && event.firstChild.childNodes.length > 2) {
      var eventEndTime = event.firstChild.childNodes[2].textContent;
    }
    else {
      let eventHeight = event.parentNode.style.height;
      let eventDuration;
      if (eventHeight === "15.4px") {
        eventDuration = 15;
      }
      else if (eventHeight === "21px") {
        eventDuration = 30;
      }
      else if (eventHeight === "31.5px") {
        eventDuration = 45;
      }
      else if (eventHeight === "42px") {
        eventDuration = 60;
      }

      let eventStartTimeHours = event.firstChild.firstChild.textContent.split(':')[0];
      let eventStartTimeMinutes = event.firstChild.firstChild.textContent.split(':')[1];
      let eventEndTimeHours = eventStartTimeHours;
      let eventEndTimeMinutes = (eventStartTimeMinutes + eventDuration) % 60;

      if (eventEndTimeMinutes <= eventStartTimeMinutes) {
        eventEndTimeHours += 1;
      }
      eventEndTime = eventEndTimeHours + ':' + eventEndTimeMinutes;
    }

    //Get number of day of the event of event in work week display mode, 1 is monday, 2 is tuesday etc
    let eventDay = Math.floor(parseInt(event.parentNode.parentNode.style.left.split('p')[0]) / 294 + 1);
    //         console.log(event);
    //         console.log(eventDay);
    //         console.log(currentDay);
    //         console.log(eventEndTime);
    //         console.log(currentHour);
    //If event was on an earlier day or ended earlier today make it transparent
    if (eventDay < currentDay || (eventDay === currentDay && Date.parse("01/01/1970 " + currentHour) > Date.parse("01/01/1970 " + eventEndTime))) {
      event.style.opacity = 0.5;
    }

    //If the correct style is not applied to the event background
    if (!re.test(event.style.background)) {
      //Retrieve the color of the event
      let eventColor = event.style.background.split("rgb(")[2].split(")")[0];
      //Set the background style to a flat version of that color
      event.style.background = "rgba(" + eventColor + ", 1) repeat scroll 0% 0%";
      //Set the background style of the events for which the previous line didn't work because their apperance is linked to the style of a child node (no idea why)
      event.firstChild.firstChild.style.background = "rgba(" + eventColor + ", 1)";
    }

    //If the correct style is not applied to the state side bar
    if (event.firstChild.firstChild.firstChild.style.background != "" && !re.test(event.firstChild.firstChild.firstChild.style.background)) {
      //Set the background style of the small "state" vertical bar
      let eventStateColor = event.firstChild.firstChild.firstChild.style.background.split("rgb(")[2].split(")")[0];
      event.firstChild.firstChild.firstChild.style.background = "rgba(" + eventStateColor + ", 1) repeat scroll 0% 0%";
    }
  });

  //Reconnect the events observer
  eventsObserver.observe(document.getElementsByClassName("ZmCalViewMgr")[0], {
    subtree: true,
    attributes: true,
    attributesFilter: ['background', 'opacity'],
    childList: true
  });

  //Stop timer
  //let time = new Date() - start;
  //Log time it took in ms
  //console.log(time + "ms");
}