Cascade Bicycle Club Add to Google Calendar

Adds a Google Calendar button to Cascade Bicycle free group rides.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Cascade Bicycle Club Add to Google Calendar
// @namespace    http://sunnyrodriguez.com
// @version      0.1
// @description  Adds a Google Calendar button to Cascade Bicycle free group rides.
// @author       splttingatms
// @match        https://www.cascade.org/node/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var ride = parseRidePage();
    var calendarEvent = toCalendarEvent(ride);
    var button = createGoogleCalendarButton(calendarEvent);
    getFirstElementByClassName(document, "sharethis-wrapper").appendChild(button);

    function buildUrl(url, parameters){
        var keyValues = [];
        for(var key in parameters) {
            if (parameters.hasOwnProperty(key)) {
                var value = parameters[key];
                keyValues.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
            }
        }

        if (keyValues.length > 0){
            url = url + "?" + keyValues.join("&");
        }

        return url;
    }

    function parseTime(timeString) {
        if (timeString === '') return null;

        var time = timeString.match(/(\d+)(:(\d\d))?\s*(p?)/i);
        if (time === null) return null;

        var hours = parseInt(time[1],10);
        if (hours == 12 && !time[4]) {
            hours = 0;
        }
        else {
            hours += (hours < 12 && time[4])? 12 : 0;
        }
        var d = new Date();
        d.setHours(hours);
        d.setMinutes(parseInt(time[3],10) || 0);
        d.setSeconds(0, 0);
        return d;
    }

    function toYYYYMMDDHHMMSSFormat(date) {
        function pad2(n) {  // always returns a string
            return (n < 10 ? '0' : '') + n;
        }

        return date.getFullYear() +
            pad2(date.getMonth() + 1) +
            pad2(date.getDate()) + "T" +
            pad2(date.getHours()) +
            pad2(date.getMinutes()) +
            pad2(date.getSeconds());
    }

    function getFirstElementByClassName(root, name) {
        var elements = root.getElementsByClassName(name);
        if (elements.length < 1)
            throw "an element with class name \"" + name + "\" does not exist";
        return elements[0];
    }

    function getFieldElement(fieldName) {
        try {
            var field = getFirstElementByClassName(document, fieldName);
            return getFirstElementByClassName(field, "field-items");
        } catch (err) {
            throw "a field with name \"" + fieldName + "\" does not exist";
        }
    }

    function getFieldElementOrDefault(fieldName) {
        try {
            return getFieldElement(fieldName);
        } catch (err) {
            console.warn(err);
            return null;
        }
    }

    function getFieldValue(fieldName) {
        return getFieldElement(fieldName).innerText.trim().replace(/\r\n|\r|\n/g, " ").trim();
    }

    function getFieldValueOrDefault(fieldName) {
        try {
            return getFieldValue(fieldName);
        } catch (err) {
            console.warn(err);
            return null;
        }
    }

    function toCalendarEvent(ride) {
        var description = "Ride URL: " + ride.url;
        if (ride.distanceText !== null) description += "\nDistance: " + ride.distanceText;
        if (ride.elevationGain !== null) description += "\nElevation Gain: " + ride.elevationGain;
        if (ride.paces !== null) description += "\nPace(s): " + ride.paces;
        if (ride.terrain !== null) description += "\nTerrain: " + ride.terrain;
        if (ride.regroup !== null) description += "\nRegroup: " + ride.regroup;
        if (ride.weatherCancels !== null) description += "\nWeather Cancels? " + ride.weatherCancels;
        if (ride.links !== null) description += "\nLinks: " + ride.links;
        description += "\nDescription:\n\n" + ride.description;

        return {
            title: "[" + ride.distanceValue + "mi] " + ride.title,
            dateRange: toYYYYMMDDHHMMSSFormat(ride.startDateTime) + "/" + toYYYYMMDDHHMMSSFormat(ride.endDateTime),
            location: ride.location,
            description: description
        };
    }

    function getRideDateTimeText() {
        try {
            return getFieldValue("field-name-field-global-datetime");
        } catch (err) {
            return getFirstElementByClassName(document, "date-display-single");
        }
    }

    function getRideLinksAsCommaSeparatedList() {
        try {
            var links = [];
            var anchors = getFieldElement("field-name-field-daily-links").getElementsByTagName("a");
            for (var i = 0; i < anchors.length; i++)
                links.push(anchors[i].href);
            return links.join(", ");
        } catch (err) {
            return null;
        }
    }

    function getRideStartDateTime() {
        var dateTime = getRideDateTimeText(); // format "Friday, May 20, 2016 - 10:00am"
        var date = new Date(dateTime.split("-")[0].trim());
        var time = parseTime(dateTime.split("-")[1].trim());
        var startDateTime = new Date(date.setHours(time.getHours()));
        return startDateTime;
    }

    function addHours(dateTime, n) {
        return new Date(new Date(dateTime).setHours(dateTime.getHours() + 3));
    }

    function parseRidePage() {
        try {
            var rideLink = window.location.href;
            var name = getFirstElementByClassName(document, "page-title").innerText.trim();
            var location = getFieldValue("field-name-field-daily-locations").replace("Directions", "").trim();
            var distanceText = getFieldValue("field-name-field-daily-distance");
            var distanceValue = Number(distanceText.split(" ")[0]); // text is a string with format "XX.XX miles"
            var rideDescription = getFieldValue("field-type-text-with-summary");
            var startDateTime = getRideStartDateTime();
            var endDateTime = addHours(startDateTime, 3); // assume rides take 3hrs

            // non-critical ride properties
            var contactInformation = getFieldValueOrDefault("field-name-field-global-contacts");
            var elevationGain = getFieldValueOrDefault("field-name-field-daily-elevation");
            var paces = getFieldValueOrDefault("field-name-field-daily-pace");
            var terrain = getFieldValueOrDefault("field-name-field-daily-terrain");
            var regroup = getFieldValueOrDefault("field-name-field-daily-regroup");
            var weatherCancels = getFieldValueOrDefault("field-name-field-daily-weather");
            var links = getRideLinksAsCommaSeparatedList();

            return {
                url: rideLink,
                title: name,
                startDateTime: startDateTime,
                endDateTime: endDateTime,
                location: location,
                contactInformation: contactInformation,
                distanceText: distanceText,
                distanceValue: distanceValue,
                elevationGain: elevationGain,
                paces: paces,
                terrain: terrain,
                regroup: regroup,
                weatherCancels: weatherCancels,
                description: rideDescription,
                links: links,
            };
        } catch (err) {
            throw "could not parse page: " + err;
        }
    }

    function toGoogleCalendarAddUrl(event) {
        return buildUrl("http://www.google.com/calendar/event", {
            action: "TEMPLATE",
            dates: calendarEvent.dateRange,
            text: calendarEvent.title,
            details: calendarEvent.description,
            location: calendarEvent.location,
            trp: false});
    }

    function createGoogleCalendarButton(event) {
        var image = document.createElement("img");
        image.src = "//www.google.com/calendar/images/ext/gc_button1.gif";

        var link = document.createElement("a");
        link.href = toGoogleCalendarAddUrl(calendarEvent);
        link.target = "_blank"; // open link in new window
        link.style = "text-decoration:none; border:none;";
        link.appendChild(image);

        var paragraph = document.createElement("p");
        paragraph.appendChild(link);

        return paragraph;
    }
})();