Skip to content

Instantly share code, notes, and snippets.

@fqj1994
Last active December 31, 2022 04:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fqj1994/87ddd40413d70fa99cdbfafda9732d34 to your computer and use it in GitHub Desktop.
Save fqj1994/87ddd40413d70fa99cdbfafda9732d34 to your computer and use it in GitHub Desktop.
Eventernote Helper Userscript
// ==UserScript==
// @name Eventernote Helper
// @namespace eventernotehelper
// @version 0.46
// @description eventernote helper to manage schedule easier.
// @author fqj1994
// @match *://www.eventernote.com/*
// @grant GM_xmlhttpRequest
// @updateURL https://gist.github.com/fqj1994/87ddd40413d70fa99cdbfafda9732d34/raw/eventernote.user.js
// @downloadURL https://gist.github.com/fqj1994/87ddd40413d70fa99cdbfafda9732d34/raw/eventernote.user.js
// ==/UserScript==
let myAttendedEvents = undefined;
let oshi = [];
function initTools(done) {
loadIcs(function() {
loadOshi(done);
});
}
function loadIcs(done) {
GM_xmlhttpRequest({
method: "GET",
url: "/users/ical",
onload: function(response) {
let startPos = response.responseText.indexOf('https://www.eventernote.com/users/calendar');
let urlStart = response.responseText.substr(startPos);
let endPos = urlStart.indexOf('"');
let url = urlStart.substr(0, endPos);
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(response) {
myAttendedEvents = response.responseText.split("\BEGIN:VEVENT");
done();
}
});
},
});
}
function loadOshi(done) {
GM_xmlhttpRequest({
method: "GET",
url: "/users/",
onload: function(response) {
let startPos = response.responseText.indexOf('gb_actors_list')
if (startPos < 0) return done();
let strip = response.responseText.substr(startPos);
let divPos = strip.indexOf('</div>');
strip = strip.substr(0, divPos);
while (true) {
let pos = strip.indexOf('<a href="/actors/');
if (pos < 0) break;
strip = strip.substr(pos);
pos = strip.indexOf('</a>');
oshi.push(strip.substr(0, pos + 4));
strip = strip.substr(pos + 4);
}
done();
},
});
}
function dist(s1, s2) {
let f = [];
for(let i = 0; i <= s2.length; i++){
f[i] = [i];
if(i === 0) continue;
for(let j = 0; j <= s1.length; j++){
f[0][j] = j;
if(j === 0) continue;
f[i][j] = s2[i-1] == s1[j-1] ? f[i - 1][j - 1] : Math.min(
f[i-1][j-1] + 1,
f[i][j-1] + 1,
f[i-1][j] + 1
);
}
}
return f[s2.length][s1.length];
}
function icsToEventName(idx, html) {
const st = html ? "\nDESCRIPTION:" : "\nSUMMARY:";
const summaryPos = myAttendedEvents[idx].indexOf(st);
const summaryStr = myAttendedEvents[idx].substr(summaryPos + st.length);
const nlPos = summaryStr.indexOf("\n");
return summaryStr.substr(0, nlPos).replace("<a href=", "<a style=\"color: DeepPink\" target=\"_blank\" href=");
}
function icsToDate(idx) {
const search = ["\nDTSTART;VALUE=DATE:", "\nDTSTART;TZID=Asia/Tokyo:"];
for (let i = 0; i < search.length; i++) {
const searchStr = search[i];
const pos = myAttendedEvents[idx].indexOf(searchStr);
if (pos >= 0) {
const sub = myAttendedEvents[idx].substr(pos + searchStr.length);
const yr = sub.substr(0, 4), mon = sub.substr(4, 2), d = sub.substr(6, 2);
return yr + "-" + mon + "-" + d;
}
}
return "";
}
function icsToDateTime(idx) {
const search = ["\nDTSTART;VALUE=DATE:", "\nDTSTART;TZID=Asia/Tokyo:"];
for (let i = 0; i < search.length; i++) {
const searchStr = search[i];
const pos = myAttendedEvents[idx].indexOf(searchStr);
if (pos >= 0) {
const sub = myAttendedEvents[idx].substr(pos + searchStr.length);
const nl = sub.indexOf("\n");
return sub.substr(0, nl);
}
}
return "";
}
function doIAttend(eventId, yes, no) {
for (let i = 0; i < myAttendedEvents.length; i++) {
if (myAttendedEvents[i].indexOf("\nURL;VALUE=URI:https://www.eventernote.com/events/" + eventId + "\r") >= 0) {
yes();
return;
}
}
no();
}
function doIConflict(Y, M, D, skipEventId, allowSimilar, yes, no) {
const pad = function(num, size){ return ('000000000' + num).substr(-size); }
const eventName = function(idx) {
return icsToEventName(idx, true);
}
const ymd = pad(Y, 4) + pad(M, 2) + pad(D, 2);
let conflict = []
let curEventIdx = -1;
for (let i = 0; i < myAttendedEvents.length; i++) {
if (myAttendedEvents[i].indexOf("\nURL;VALUE=URI:https://www.eventernote.com/events/" + skipEventId + "\r") >= 0) {
curEventIdx = i;
}
}
for (let i = 0; i < myAttendedEvents.length; i++) {
if (myAttendedEvents[i].indexOf("\nURL;VALUE=URI:https://www.eventernote.com/events/" + skipEventId + "\r") >= 0) {
continue;
}
if (myAttendedEvents[i].indexOf("\nDTSTART;VALUE=DATE:" + ymd) >= 0) {
if (allowSimilar && curEventIdx >= 0 && dist(icsToEventName(i, false), icsToEventName(curEventIdx, false)) <= 5) {
continue;
}
conflict.push(eventName(i));
}
if (myAttendedEvents[i].indexOf("\nDTSTART;TZID=Asia/Tokyo:" + ymd + "T") >= 0) {
if (allowSimilar && curEventIdx >= 0 && dist(icsToEventName(i, false), icsToEventName(curEventIdx, false)) <= 5) {
continue;
}
conflict.push(eventName(i));
}
}
conflict.sort();
if (conflict.length > 0) yes(conflict);
else no();
}
function exportItemToText(future_only) {
let events = []
let now = new Date().toISOString();
for (let idx = 0; idx < myAttendedEvents.length; idx++) {
if (icsToDate(idx) != "" && (!future_only || icsToDate(idx) >= now)) {
events.push(icsToDateTime(idx) + "\t" + icsToDate(idx) + "\t" + icsToEventName(idx, false));
}
}
events.sort();
for (let idx = 0; idx < events.length; idx++) {
events[idx] = events[idx].split("\t").slice(1).join("\t");
}
const dataURL = 'data:text/plain,' + encodeURIComponent(events.join("\n"));
const pageHeight = Math.max( document.body.scrollHeight, document.body.offsetHeight,
document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight );
document.body.innerHTML += '<div id="calData" style="width: 100%; height: ' + pageHeight + 'px; position: absolute; left: 0px; top: 0px; background-color: white;"></div>'
document.getElementById('calData').innerHTML = "<pre id=\"pressesc\">Escを押すと、Eventernoteに戻ります。了解を押すと、この部分が消えます。<input type=\"button\" value=\"了解\" onclick=\"document.getElementById('pressesc').style='display: none;';\"/></pre>" +
"<pre>" + events.join("\n") + "</pre>";
document.onkeydown = function(evt) {
evt = evt || window.event;
let isEscape = false;
if ("key" in evt) {
isEscape = (evt.key == "Escape" || evt.key == "Esc");
} else {
isEscape = (evt.keyCode == 27);
}
if (isEscape) {
document.onkeydown = undefined;
document.getElementById('calData').outerHTML = "";
document.getElementById("exportToText").addEventListener("click", exportToText, false);
document.getElementById("exportAllToText").addEventListener("click", exportAllToText, false);
}
}
}
function exportToText() {
exportItemToText(true);
}
function exportAllToText() {
exportItemToText(false);
}
function markEventStatus(eventNode, eventId, Y, M, D) {
let colorNode = eventNode.getElementsByClassName('event')[0];
let dataNode = eventNode.getElementsByClassName('event')[0];
if (eventNode.parentNode.tagName == 'A') {
colorNode = eventNode.parentNode;
dataNode = eventNode.parentNode;
}
colorNode.style = 'background-color: LemonChiffon';
doIAttend(
eventId,
function() {
colorNode.style = 'background-color: Aqua';
doIConflict(
Y, M, D, eventId, true,
function(conflictEvents) {
colorNode.style = 'background-color: Orange';
dataNode.innerHTML += ("<div>" + conflictEvents.join("<br/>") + "</div>");
},
function() {});
},
function() {
doIConflict(
Y, M, D, eventId, false,
function(conflictEvents) {
colorNode.style = 'background-color: LightPink';
dataNode.innerHTML += ("<div>" + conflictEvents.join("<br/>") + "</div>");
},
function() {
colorNode.style = '';
});
}
);
}
function handleOshi(node) {
for (let oshiIdx = 0; oshiIdx < oshi.length; oshiIdx++) {
node.innerHTML = node.innerHTML.replace(oshi[oshiIdx], oshi[oshiIdx].replace('<a ', '<a style="font-size: 20px; color: red" '));
}
}
(function() {
'use strict';
initTools(function() {
if (document.getElementsByClassName('dropdown-menu').length > 0) {
document.getElementsByClassName('dropdown-menu')[0].innerHTML += '<li><a id="exportToText">これからの予定(文字のみ)</a></li>'
          document.getElementsByClassName('dropdown-menu')[0].innerHTML += '<li><a id="exportAllToText">全てのイベント(文字のみ)</a></li>'
document.getElementById("exportToText").addEventListener("click", exportToText, false);
document.getElementById("exportAllToText").addEventListener("click", exportAllToText, false);
}
[].forEach.call(document.getElementsByClassName('gb_listview'), handleOshi);
let path = document.location.pathname;
let pathSplited = path.split('/');
let lastPart = pathSplited[pathSplited.length - 1];
if (pathSplited[pathSplited.length - 2] == 'events' && path.indexOf('/events/') == 0 && parseInt(lastPart).toString() == lastPart) {
let going = document.firstElementChild.innerHTML.indexOf('<p class="center note-edit-area">') >=0 || document.firstElementChild.innerHTML.indexOf('ノートを編集</a></p>') >= 0;
let eventDateNode = undefined;
let dataNode = undefined;
let mobile = false;
if (document.getElementsByClassName('gb_events_info_table')[0]) {
dataNode = document.getElementsByClassName('gb_events_info_table')[0].firstElementChild.firstElementChild;
eventDateNode = document.getElementsByClassName('gb_events_info_table')[0].firstElementChild.firstElementChild.firstElementChild.lastElementChild;
} else {
dataNode = document.getElementsByClassName('mod_page')[0].childNodes[7]
eventDateNode = document.getElementsByClassName('mod_page')[0].childNodes[7];
mobile = true;
}
let eventDate = eventDateNode.innerText;
let dateArray = eventDate.split(' ')[0].split('-');
let dateY = parseInt(dateArray[0]), dateM = parseInt(dateArray[1]), dateD = parseInt(dateArray[2]);
doIConflict(
dateY, dateM, dateD, lastPart, false,
function(conflictEvents) {
if (!going) {
eventDateNode.innerHTML +=
"❌";
}
dataNode.innerHTML =
(mobile ? "" : "<tr><td class=\"span2\">その日に" + (going ? "<strong>も</strong>" : "") + "参加するイベント</td><td>") +
conflictEvents.join("<br/>") + (mobile ? "<br/>" : "</td></tr>") +
dataNode.innerHTML;
},
function() {
document.getElementsByClassName('gb_events_info_table')[0].firstElementChild.firstElementChild.firstElementChild.lastElementChild.innerHTML +=
"✅";
}
);
return;
}
let events = [];
let eventListDiv = document.getElementsByClassName('gb_event_list');
for (let idx = 0; idx < eventListDiv.length; idx++) {
let clearFixBlock = eventListDiv[idx].getElementsByClassName('clearfix');
for (let idx2 = 0; idx2 < clearFixBlock.length; idx2++) {
events.push(clearFixBlock[idx2]);
}
}
let mobileEventListDiv = document.getElementsByClassName('gb_listevent');
for (let idx = 0; idx < mobileEventListDiv.length; idx++) {
let boxBlock = mobileEventListDiv[idx].getElementsByClassName('box');
for (let idx2 = 0; idx2 < boxBlock.length; idx2++) {
events.push(boxBlock[idx2]);
}
}
for (let idx = 0; idx < events.length; idx++) {
let event = events[idx];
handleOshi(event);
let date = event.getElementsByClassName('date')[0];
let dateStr = date.firstElementChild.innerText;
let dateArray = dateStr.split(' ')[0].split('-');
let dateY = parseInt(dateArray[0]), dateM = parseInt(dateArray[1]), dateD = parseInt(dateArray[2]);
let eventUrl = undefined;
if (event.getElementsByClassName('event')[0].firstElementChild.firstElementChild)
eventUrl = event.getElementsByClassName('event')[0].firstElementChild.firstElementChild.href;
else
eventUrl = event.parentNode.href;
let eventUrlSplit = eventUrl.split('/');
let eventId = parseInt(eventUrlSplit[eventUrlSplit.length - 1]);
markEventStatus(event, eventId, dateY, dateM, dateD);
let noteCount = event.getElementsByClassName('note_count');
if (noteCount.length > 0) {
let noteCountEle = noteCount[0];
if (noteCountEle.firstElementChild.title == '参加者数') {
noteCountEle.addEventListener('click', function() {
GM_xmlhttpRequest({
method: 'GET',
url: eventUrl,
onload: function(response) {
let text = response.responseText;
let start = text.indexOf('このイベントに参加のフレンズ');
let end = text.indexOf('このイベントに参加のイベンター');
if (start == -1 || end == -1) return;
let con = text.substr(start, end - start + 1);
let people = [];
while (true) {
let start = con.indexOf('<p class="name pre">');
if (start < 0) break;
con = con.substr(start);
let end = con.indexOf('</p>');
people.push(con.substr('<p class="name pre">'.length, end - '<p class="name pre">'.length));
con = con.substr(end + 1);
}
event.getElementsByClassName('event')[0].innerHTML += '<div>このイベントに参加のフレンズ<br/>' + people.join('\n') + '</div>';
}
});
});
}
}
}
let actors = document.getElementsByClassName('gb_actors_list')[0].getElementsByTagName("li");
for (let idx = 0; idx < actors.length; idx++) {
let style_str = actors[idx].getAttribute('class');
if (!style_str) continue;
let styles = style_str.split(" ");
let cnt = undefined;
for (let tmp = 0; tmp < styles.length; tmp++) {
if (styles[tmp].match(/^c/)) cnt = parseInt(styles[tmp].substr(1));
}
actors[idx].innerHTML += '<span style="font-size: 14px"> (' + cnt + ')</span>';
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment