Closed balta2ar closed 4 years ago
Here's a userscript that works 💓
// ==UserScript==
// @name google-calendar-automate-duplication
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Automate duplication of events
// @author Yuri Bochkarev
// @match https://calendar.google.com/calendar/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const DELAY = 500;
const STYLE_ACTIVE = "margin-left: 10px; font-size: 20px; font-weight: bold; color: red;";
const STYLE_INACTIVE = "margin-left: 10px; font-size: 20px; font-weight: normal; color: grey;";
//var currentWeek;
var currentDay;
//var toggle;
var isActive = false;
function createToggle() {
let header = document.querySelector('[aria-label="Calendar"]');
let d = document.createElement('a');
d.style = STYLE_INACTIVE;
d.onclick = function() {
isActive = !isActive;
if (isActive) { this.style = STYLE_ACTIVE; }
else { this.style = STYLE_INACTIVE; }
}
d.innerHTML = 'D'
header.appendChild(d);
return d
}
function getCurrentWeek() {
for (let div of document.querySelectorAll('div')) {
if (div.innerText.startsWith('Week ')) {
return div.innerText;
}
}
return null;
}
function getCurrentDay() {
let mini = document.querySelector('[data-month]')
if (mini) return mini.querySelector('[aria-selected=true]');
}
function app() {
createToggle();
var saveClicker = setInterval(function () {
if (!isActive) return;
var saveButton = document.querySelector('div[aria-label=Save]');
if (saveButton == null) return;
saveButton.click();
setTimeout(function() { currentDay.click(); }, DELAY);
//var scrollToCurrentWeek = setInterval(function() {
// if (currentWeek == getCurrentWeek()) { clearInterval(scrollToCurrentWeek); return; }
// var nextWeekButton = document.querySelector('div[aria-label="Next week"]');
// if (nextWeekButton) { nextWeekButton.click(); return; }
// clearInterval(scrollToCurrentWeek);
//}, 500);
}, DELAY);
var simulateClick = function(element) {
// Simulate mouse down
element.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, view: window }));
// Simulate mouse release
element.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true, view: window }))
}
var optionsClicker = setInterval(function () {
if (!isActive) return;
var optionsButton = document.querySelector('div[aria-label=Options]');
var duplicateButton = document.querySelector('span[aria-label=Duplicate]');
if (optionsButton != null && duplicateButton == null) {
optionsButton.click();
} else if (optionsButton != null && duplicateButton != null) {
//currentWeek = getCurrentWeek();
currentDay = getCurrentDay();
simulateClick(duplicateButton);
}
}, DELAY);
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
app();
} else {
document.addEventListener("DOMContentLoaded", app);
}
})();
Hi, thank you for opening the issue. Yes, triggering the click event on some buttons is a bit tricky on Google Calendar :)
I'll look into this and see if I can reproduce the bug after my exams next week. I'll keep you posted.
I found this question (https://stackoverflow.com/questions/50095952/javascript-trigger-jsaction-from-chrome-console) on how to trigger a click in google's jsaction library, but there is no answer and I have no idea either (the regular click
event sometimes doesn't work).
My awesome colleague cracked the problem and the following click simulation works with Duplicate menu entry, now it's clickable:
// Simulate mouse down
element.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, view: window }));
// Simulate mouse release
element.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true, view: window }))
The only downside that I see now is that after Save button is pressed, the app scrolls back to the current week (I usually duplicate events of the following week).
This is awesome! I'll be happy to implement this next week . If you'd like to put together a PR for this i'll be happy to merge it.
The scroll to the current week is something I have noticed too (see #2 ). I have yet to implement a workaround for it. But maybe, with this click event dispatch, I am going to be able to trigger click event on the current day button in the mini calendar!
trigger click event on the current day button in the mini calendar
But that will bring you back to the current day. Since I only use "Week" layout, I guess for me it will work if I remember the current (Week X label in the top bar) and if I keep clicking "Next week" button until the current week label matches.
I keep clicking "Next week" button until the current week label matches
It worked just fine. Here is an excerpt:
var currentWeek;
var saveClicker = setInterval(function () {
var saveButton = document.querySelector('div[aria-label=Save]');
if (saveButton == null) return;
saveButton.click();
var scrollToCurrentWeek = setInterval(function() {
if (currentWeek == getCurrentWeek()) { clearInterval(scrollToCurrentWeek); return; }
var nextWeekButton = document.querySelector('div[aria-label="Next week"]');
if (nextWeekButton) { nextWeekButton.click(); return; }
clearInterval(scrollToCurrentWeek);
}, 500);
}, 500);
...
currentWeek = getCurrentWeek();
simulateClick(duplicateButton);
I've also updated the full userscript. Jesus, gcalendar is usable at last!
But that will bring you back to the current day.
I don't quite understand what do you mean here. The workflow i have in mind to fix the problem is this:
Here's an example of the mini calendar with the highlighted day (highlighted day is 18, current day is 4):
clicks it (this will jump back to the original week)
This is weird: I tried exactly your approach before you described it and why it didn't work for me was that the layout changed from Week to Day, thus I naturally thought that whenever you click a day on a mini sidebar, you go to Day view. But now I try it again, and Week layout is preserved, so clearly your approach if both faster and more robust! :+1:
I will implement this next week then. :)
Just to make sure we don't waste time duplicating the work, are you planning to put together a PR for this?
No, I think I'll keep it as my userscript for now because it would be easier for me to hack new things if I have other ideas. Thank you for giving me inspiration!
I've updated my previous comment with the latest version, added your go-to-current-day approach and a nice toggle button to enable/disable duplicator functionality on the fly.
Hi @balta2ar, I updated the extension to version 1.0.2 and fixed the duplication of the event following your suggestions. This should work correctly now. I also fixed the jumping to the current day when duplicating an event on a week other than the current (See #2 ). Could you please check if this could work for you?
Hi! When I click "duplicate" button, I see that location in the window updates to something like this:
https://calendar.google.com/calendar/r/eventedit/duplicate/<some-long-string-with-random-characters>#duplicate
, but after that nothing happens. As far as I understand, the extension awaits "Duplicate" window to pup up and clicks Save. But that doesn't happen.EDIT: if I manually click "Options -> Duplicate", it finds the window and immediately clicks Save, which is sort of a good thing. However, this prevents from editing any events because editing window is instantly closed, event if I didn't click Duplicate.
It makes more sense to enable Edit window search until it's found, and then switch to idling state until Duplicate button is pressed again.
Maybe this extension could be implemented as a Tampermonkey script as well so that you don't have to wait for the Google review to complete before publishing.