Skip to content

Instantly share code, notes, and snippets.

@arantius
Created December 7, 2012 04:45

Revisions

  1. arantius created this gist Dec 7, 2012.
    145 changes: 145 additions & 0 deletions ingress-intel-notify-ui.user.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,145 @@
    // ==UserScript==
    // @name Ingress Intel: Notify UI
    // @namespace https://arantius.com/misc/greasemonkey/
    // @description Annotate the Ingress Intel Dashboard with links to control the Ingress Notify app.
    // @match http://www.ingress.com/intel*
    // @version 1
    // @grant none
    // ==/UserScript==

    //const NOTIFY_SERVER = 'http://localhost:8080/'
    //const NOTIFY_SERVER = 'https://ingress-jabber.appspot.com/'
    const NOTIFY_SERVER = 'https://ingress-notify.appspot.com/'

    const CLICKY_TEMPLATE =
    "<a href='javascript:doWatch(true);void(0)' style='color: #11ECF7'>Watch</a> | "
    +"<a href='javascript:doWatch(false);void(0)' style='color: #11ECF7'>Unwatch</a>";

    window.lastClickedMarker = null;
    window.lastOpenedTitle = null;


    // Find the global function that creates the Marker instances.
    // It's the only one whose source mentions "new google.maps.Marker".
    for (i in window) {
    if (!window.hasOwnProperty(i)) continue;
    if ('function' != typeof window[i]) continue;
    if (-1 == window[i].toString().indexOf('new google.maps.Marker')) continue;

    // We found it! Inject our shim in place of it.
    overrideMarkerCreator(window[i], i);
    break;
    }


    function overrideMarkerCreator(origMarkerCreator, prop) {
    // The original function takes one argument "a" and contains a line like:
    // ... a.G = new google.maps.Marker( ...
    // Find that line, and the property name that the marker is assigned to.
    var m = origMarkerCreator.toString().match(
    /\.([^ ]+) = new google.maps.Marker/);
    if (!m) {
    // Crap something didn't work right!
    console.error('Could not find the Marker instantiation in:',
    origMarkerCreator.toString());
    return;
    } else {
    // Inject a replacement function which ...
    window[prop] = function(a) {
    // ... calls the original function ...
    origMarkerCreator(a);
    // ... then pulls out the marker instance, from the property found above.
    onHaveMarker(a[m[1]]);
    }
    }
    }


    function onHaveMarker(marker) {
    google.maps.event.addListener(marker, 'click', function() {
    window.lastClickedMarker = marker;
    });
    }


    // Find the global function that creates the popup contents.
    // It's the only one whose source mentions "portal_primary_title".
    for (i in window) {
    if (!window.hasOwnProperty(i)) continue;
    if ('function' != typeof window[i]) continue;
    if (-1 == window[i].toString().indexOf('portal_primary_title')) continue;

    // We found it! Inject our shim in place of it.
    overridePopupContent(window[i], i);
    break;
    }


    function overridePopupContent(origPopupContent, prop) {
    // Create a new function which ...
    window[prop] = function(a, b) {
    // ... calls the original function ...
    var html = origPopupContent(a, b);

    // ... then parse and mutate HTML with regexes! ...
    var m = html.match(/<div id="portal_primary_title">(.*?)</);
    if (!m) return html;
    window.lastOpenedTitle = m[1];
    html = html.replace(
    /<div( id="portal_level">.*?)<\/div>/,
    CLICKY_TEMPLATE + ' <span$1</span>');

    // ... and sends it on its merry way.
    return html;
    }
    }


    window.doWatch = function(watched) {
    var pos = lastClickedMarker.getPosition();
    var lat = Math.floor(pos.lat() * 1e6);
    var lng = Math.floor(pos.lng() * 1e6);
    var url = NOTIFY_SERVER + 'portals/' + lat + ',' + lng;

    var request = new XMLHttpRequest();
    request.open('PUT', url, true);
    request.withCredentials = true;

    /*
    // The error handler is called even in case of success?!
    request.addEventListener('load', function() {
    console.log('xhr onload!');
    showMessage(
    (watched ? 'Watching' : 'Unwatched')
    + ' portal ' + window.lastOpenedTitle);
    }, false);
    request.addEventListener('error', function(event) {
    console.log(request, event);
    showMessage('Error communicating with server!');
    }, false);
    document.getElementById('map_spinner').style.display = 'block';
    */

    request.send(
    JSON.stringify({
    'title': window.lastOpenedTitle,
    'latE6': lat,
    'lngE6': lng,
    'address': null,
    'watched': watched,
    })
    );
    };

    var butterHideTimeout;
    function showMessage(msg) {
    document.getElementById('map_spinner').style.display = 'none';

    clearTimeout(butterHideTimeout);
    var butterbar = document.getElementById('butterbar');
    butterbar.textContent = msg;
    butterbar.style.display = 'inherit';
    butterHideTimeout = setTimeout(function() {
    butterbar.style.display = 'none';
    }, 10000);
    }