NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// Copyright (C) 2010-2015 Matthias Hoefel & Robert Walter
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
// ======================================================================
// ==UserScript==
// @name Gc2OcLink
// @description Gc2OcLink provides a link to a opencaching.de listing from a geocaching.com listing. The user has to install a greasemonkey script to add the link to the GC listing page.
// @copyright 2010-2015 Matthias Hoefel & Robert Walter, special thanks to Michael Walter
// @license GPLv3 , http://www.gnu.org/copyleft/gpl.html
// @version 0.3.2
// @oujs:author Metrax
// @homepageURL https://openuserjs.org/scripts/Metrax/Gc2OcLink
// @include http://www.geocaching.com/seek/cache_details*
// @include http*://www.geocaching.com/my/*
// @include http*://www.geocaching.com/bookmarks/view*
// @include http*://www.geocaching.com/seek/nearest*
// @include http*://www.geocaching.com/geocache/*
// @include http*://www.geocaching.com/play/search/*
// @grant GM_xmlhttpRequest
// @updateURL https://openuserjs.org/meta/Metrax/Gc2OcLink.meta.js
// @downloadURL https://openuserjs.org/install/Metrax/Gc2OcLink.user.js
// ==/UserScript==
var VERSION = "0.3.2";
var DEBUG = false;
var LABEL_HEADER = "Also listed at";
if (document.URL.search("www\.geocaching\.com\/my\/") >= 0) {
modifyMyProfile();
} else if (document.URL.search("\/geocache\/") >= 0) {
modifyCacheDetails();
} else if (document.URL.search("www\.geocaching\.com\/bookmarks\/view\.aspx") >= 0) {
modifyBookmarkList();
} else if (document.URL.search("www\.geocaching\.com\/seek\/nearest\.aspx") >= 0) {
modifySearchResultList();
} else if (document.URL.search("www\.geocaching\.com\/play\/search\/") >= 0) {
modifyNewSearchResultList();
}
function modifyMyProfile() {
debug("modify my profile");
modifyProfileSideBar();
// addListColumn();
}
/**
* Modifies the right hand side bar of the profile. Currently adds a simple
* version info.
*/
function modifyProfileSideBar() {
var gc2ocDiv = document.createElement("div");
gc2ocDiv.setAttribute("class", "YourProfileWidget");
gc2ocDiv.setAttribute("id", "gc2ocLinkDiv");
var gc2ocHeader = document.createElement("h3");
gc2ocHeader.setAttribute("class", "WidgetHeader");
gc2ocHeader.innerHTML = "gc2oc Link";
gc2ocDiv.appendChild(gc2ocHeader);
var gc2ocBody = document.createElement("div");
gc2ocBody.setAttribute("class", "WidgetBody");
gc2ocBody.setAttribute("id", "gc2ocLinkDivBody");
var spanElement = document.createElement("span");
spanElement.appendChild(document.createTextNode("Version: " + VERSION));
gc2ocBody.appendChild(spanElement);
gc2ocDiv.appendChild(gc2ocBody);
// The favorite widget has a id, so we use it to get the parent element.
// Any other widget with an id would do the job as well.
var favoritesWidget = document.getElementById("ctl00_ContentBody_pnlFavoritesWidget");
favoritesWidget.parentNode.appendChild(gc2ocDiv);
}
function addListColumn() {
// The favorite widget has a id, so we use it to get the parent element.
// Any other widget with an id would do the job as well.
var favoritesWidget = document.getElementById("ctl00_ContentBody_pnlFavoritesWidget");
debug("entrypoint found");
// Search the div that contains the table by walking backwards through the
// list of divs by using previouseSibling
var tableEnclosingDiv = favoritesWidget.parentNode.previousSibling;
while (!tableEnclosingDiv.attributes || tableEnclosingDiv.getAttribute("class") != "yui-u first") {
tableEnclosingDiv = tableEnclosingDiv.previousSibling;
}
if (!tableEnclosingDiv) {
debug("table enclosing div not found");
return;
}
debug("table enclosing div found");
debug("tableEnclosingDiv = " + tableEnclosingDiv);
debug("tableEnclosingDiv = " + tableEnclosingDiv.childNodes);
var tableElement;
var childNodes = tableEnclosingDiv.childNodes;
var nodeName = "TABLE";
for ( var i = 0; i < childNodes.length; i++) {
debug("childNodes[i].nodeName = " + childNodes[i].nodeName);
if (childNodes[i].nodeName == nodeName) {
tableElement = childNodes[i];
break;
}
}
// search for the table
debug("table = " + tableElement);
while (tableElement.nodeName != nodeName) {
tableElement = tableElement.nextSibling;
debug("table = " + tableElement);
}
if (tableElement == null) {
debug("table not found");
return;
}
debug("table found");
var tableRows = tableElement.getElementsByTagName("TR");
for ( var i = 0; i < tableRows.length; i++) {
var cell = document.createElement("td");
cell.appendChild(document.createTextNode("Hallo!"));
tableRows[i].appendChild(cell);
}
debug("done");
}
/**
* Modifies the cache details page. Adds a box with the oc link.
*/
function modifyCacheDetails() {
debug("modify cache details");
var go2olpDiv = document.createElement("div");
go2olpDiv.setAttribute("class", "CacheDetailNavigationWidget Spacing");
var go2olpHeader = document.createElement("h3");
go2olpHeader.setAttribute("class", "WidgetHeader");
go2olpHeader.setAttribute("style", "margin-top:1em; font-size:100%");
go2olpHeader.innerHTML = "<strong>" + LABEL_HEADER + "</strong>";
go2olpDiv.appendChild(go2olpHeader);
var go2olpBody = document.createElement("div");
go2olpBody.setAttribute("class", "WidgetBody");
go2olpBody.setAttribute("id", "go2olpBody");
//var cacheCodeWidget = document.getElementById("ctl00_ContentBody_uxWaypointName");
var cacheCodeWidget = document.getElementById("ctl00_ContentBody_CoordInfoLinkControl1_uxCoordInfoCode");
var gcWaypoint = getGCCOMWayPointFromElement(cacheCodeWidget);
var spanElement = createOCLink(gcWaypoint, "opencaching.de");
go2olpBody.appendChild(spanElement);
go2olpDiv.appendChild(go2olpBody);
var lnk = document.getElementById("ctl00_ContentBody_lnkTravelBugs");
lnk.parentNode.insertBefore(go2olpDiv, lnk);
}
function modifyBookmarkList() {
debug("modify bookmark list");
// Get the nearest element with an id
var abuseReportDiv = document.getElementById("ctl00_ContentBody_ListInfo_uxAbuseReport");
debug("entrypoint found: " + abuseReportDiv);
var tableTag = findFirstChildNodeByClass(abuseReportDiv.parentNode.childNodes, "Table NoBottomSpacing");
if (!tableTag) {
debug("table tag not found");
return;
}
debug("table tag: " + tableTag);
var tableHead = findFirstChildNodeByName(tableTag.childNodes, "THEAD");
var headerRow = findFirstChildNodeByName(tableHead.childNodes, "TR");
var headerCell = document.createElement("th");
headerCell.appendChild(document.createTextNode(LABEL_HEADER));
headerRow.appendChild(headerCell);
var tableRows = document.getElementsByTagName('TR');
var count=0;
for(var i=0;i<tableRows.length;i++) {
if(tableRows[i].id.match('^ctl00_ContentBody_ListInfo_BookmarkWpts_ctl.._dataRow$')) {
debug(tableRows[i].innerHTML);
addLinkColumn2BookmarkListRow(tableRows[i]);
}
}
}
function addLinkColumn2BookmarkListRow(tableRow) {
if (tableRow.attributes && tableRow.getAttribute("id")) {
// The row with a id is the first row of the two lined entry.
// dangerous! position might be subject to change!
var wayPointLink = tableRow.getElementsByTagName("TD")[3];
var gcWayPoint = getGCCOMWayPointFromElement(wayPointLink);
if(gcWayPoint != '') {
debug("gcWayPoint = " + gcWayPoint);
var cell = document.createElement("td");
if (gcWayPoint.match(/(GC[A-Z0-9]+)/)) {
cell.appendChild(createOCLink(gcWayPoint, "oc.de"));
} else {
cell.appendChild(document.createTextNode("Failed to get GC-Waypoint. Update to a new version of gc2oc link."));
}
var rowSpan = document.createAttribute("rowspan");
rowSpan.nodeValue="2";
cell.setAttributeNode(rowSpan);
tableRow.appendChild(cell);
}
}
debug("test");
}
function modifySearchResultList() {
debug("modify search result list");
var resultTable = document.getElementById("ctl00_ContentBody_ResultsPanel");
var tableBodies = resultTable.getElementsByTagName("TBODY");
// yes, we have two of them. And we need the second one.
var tableRows = tableBodies[1].getElementsByTagName("TR");
for ( var i = 0; i < tableRows.length; i++) {
var rowClass = tableRows[i].getAttribute("class");
var searchListHeaderRowClass = "BorderTop";
var ownSearchListRowClass = "TertiaryRow Data BorderTop";
var friendSearchListRowClass = "SolidRow Data BorderTop";
var friendSearchListAlternatingRowClass = "AlternatingRow Data BorderTop";
if (rowClass == ownSearchListRowClass ||
rowClass == friendSearchListRowClass ||
rowClass == friendSearchListAlternatingRowClass ||
rowClass == searchListHeaderRowClass ) {
var cell;
if (rowClass == ownSearchListRowClass ||
rowClass == friendSearchListRowClass ||
rowClass == friendSearchListAlternatingRowClass) {
cell = document.createElement("td");
// dangerous! position might be subject to change!
var wayPointCell = tableRows[i].getElementsByTagName("TD")[5];
var gcWayPoint = getGCCOMWayPointFromElement(wayPointCell);
debug("gcWayPoint = " + gcWayPoint);
// cell.appendChild(document.createTextNode(gcWayPoint));
cell.appendChild(createOCLink(gcWayPoint));
} else {
cell = document.createElement("th");
cell.appendChild(document.createTextNode(LABEL_HEADER));
var scope = document.createAttribute("scope");
scope.nodeValue="col";
cell.setAttributeNode(scope);
}
tableRows[i].appendChild(cell);
}
}
}
function modifyNewSearchResultList() {
debug("modify new search result list");
var resultTable = document.getElementById("searchResultsTable");
// Build Table Header
var tableHeads = resultTable.getElementsByTagName("THEAD");
var tableHeadRows = tableHeads[0].getElementsByTagName("TR");
var tableColGroup = resultTable.getElementsByTagName("COLGROUP");
ColCell = document.createElement("col");
tableColGroup[0].appendChild(ColCell);
HeaderCell = document.createElement("th");
HeaderCell.setAttribute("class", "sort-column");
HeaderCell.setAttribute("scope", "col");
HeaderCell.style.width = "10%";
HeaderCellText = document.createElement("a");
HeaderCellText.setAttribute("class", "outbound-link");
HeaderCellText.appendChild(document.createTextNode("OC"));
HeaderCell.appendChild(HeaderCellText);
tableHeadRows[0].appendChild(HeaderCell);
// Manipulate Cache Rows
var tableBodies = resultTable.getElementsByTagName("TBODY");
var tableRows = tableBodies[0].getElementsByTagName("TR");
for ( var i = 0; i < tableRows.length; i++) {
tableCols = tableRows[i].getElementsByTagName("TD");
if(tableCols[0].innerHTML.indexOf("Upgrade Now") != -1) {
} else {
tableColLinks = tableCols[0].getElementsByTagName("A");
tableColSpans = tableColLinks[0].getElementsByTagName("SPAN");
if(tableColSpans[1].innerHTML == "Premium" || tableColSpans[1].innerHTML == "Disabled" || tableColSpans[1].innerHTML.indexOf("*Upgrade Now*") != -1) {
CacheText = tableColSpans[3];
} else {
CacheText = tableColSpans[2];
}
CacheCode = getGCCOMWayPointFromElement(CacheText);
OCCell = document.createElement("td");
OCCell.setAttribute("class","mobile-show pri-1");
//OCCell.style.width = "10%";
OCCell.appendChild(createOCLink(CacheCode));
//OCCell.className = 'pri-1';
tableRows[i].appendChild(OCCell);
}
}
}
function getGCCOMWayPointFromElement(element) {
var regex = /(GC[A-Z0-9]{1,5})+/;
var cache = element.textContent.match(regex);
debug("Geocache: " + cache[0]);
return cache[0];
}
function getLatLonSpan() {
var latlon_span = document.getElementById('ctl00_ContentBody_LatLon');
var regex = /([NS])\s(\d{1,3}).\s(\d{1,2})\.(\d{3})\s([EW])\s(\d{1,3}).\s(\d{1,2})\.(\d{3})/;
debug("Cache coordinates:", latlon_span.textContent.match(regex));
}
function getLatitude() {
getLatLonSpan();
var lat = parseFloat(RegExp.$2) + (parseFloat(RegExp.$3) + (parseFloat(RegExp.$4) / 1000)) / 60;
if (RegExp.$1 == 'S') {
lat = -1 * lat;
}
lat = Math.round(lat * 100000) / 100000;
debug("lat=" + lat);
return lat;
}
function getLongitude() {
getLatLonSpan();
var lon = parseFloat(RegExp.$6) + (parseFloat(RegExp.$7) + (parseFloat(RegExp.$8) / 1000)) / 60;
if (RegExp.$5 == 'W') {
lon = -1 * lon;
}
lon = Math.round(lon * 100000) / 100000;
debug("lon=" + lon);
return lon;
}
/**
* Parses the value of the found flag from the request response.
*
* @param dom
* dom of the response
* @return the found flag or <code>false</code> if response was empty.
*/
function parseXML_GetFoundFlag(dom) {
var caches = dom.getElementsByTagName("cache");
if (caches.length < 1)
return false;
var foundFlag = caches[0].getAttribute("found");
return foundFlag;
}
function parseXML_GetInactiveFlag(dom) {
var caches = dom.getElementsByTagName("cache");
if (caches.length < 1)
return false;
var inactiveFlag = caches[0].getAttribute("inactive");
return inactiveFlag;
}
/**
* Parses the oc waypoint from the request response.
*
* @param dom
* dom of the response
* @return the oc waypoint or <code>false</code> if response was empty.
*/
function parseXML_GetOCwp(dom) {
var caches = dom.getElementsByTagName("cache");
if (caches.length < 1)
return false;
var wp = caches[0].getAttribute("wpoc");
return wp;
}
function debug(message) {
if (DEBUG && console) {
console.debug(message);
}
}
function createOCLink(gcWaypoint, linkLabel) {
var spanElement = document.createElement("span");
spanElement.setAttribute("id", "go2olp_ocRef");
if (linkLabel) {
var ocLinkElement = document.createElement('a');
ocLinkElement.href = "http://www.opencaching.de";
ocLinkElement.target = "_blank";
ocLinkElement.appendChild(document.createTextNode(linkLabel));
spanElement.appendChild(ocLinkElement);
spanElement.appendChild(document.createTextNode(": "));
}
var urlString = 'http://www.opencaching.de/map2.php?mode=wpsearch&wp=' + gcWaypoint;
GM_xmlhttpRequest( {
method : 'GET',
url : urlString,
timeout : 5000,
headers : {
'User-agent' : 'gc2oclink (greasemonkey)' + VERSION,
'Accept' : 'text/xml'
},
onload : function(responseDetails) {
var parser = new DOMParser();
debug(responseDetails.responseText);
var dom = parser.parseFromString(responseDetails.responseText, "text/xml");
var ocwp = parseXML_GetOCwp(dom);
if (!ocwp) {
spanElement.appendChild(document.createTextNode("not listed"));
} else {
debug('ocKey: ' + ocwp);
var linkElement = document.createElement('a');
linkElement.href = "http://www.opencaching.de/viewcache.php?wp=" + ocwp;
linkElement.target = "_blank";
linkElement.appendChild(document.createTextNode(ocwp));
var inactiveFlag = parseXML_GetInactiveFlag(dom);
if(inactiveFlag == '1') {
linkElement.style.textDecoration = "line-through";
}
spanElement.appendChild(linkElement);
var foundFlag = parseXML_GetFoundFlag(dom);
if (foundFlag && foundFlag == '1') {
spanElement.appendChild(document.createTextNode(" "));
var foundIcon = document.createElement('img');
foundIcon.src = "http://www.opencaching.de/resource2/ocstyle/images/viewcache/16x16-found.png";
foundIcon.title = "Logged on opencaching.de";
spanElement.appendChild(foundIcon);
}
}
},
ontimeout : function (responseDetails) {
spanElement.appendChild(document.createTextNode("Connection error"));
},
onerror : function (responseDetails) {
spanElement.appendChild(document.createTextNode("Connection error"));
}
});
return spanElement;
}
/**
* Finds a child node by its name. Simply walks through the child array and
* compares the node names.
*
* @param childNodes
* Array of nodes to search through
* @param nodeName
* Name of the node to search
* @return node found or <code>null</code>
*/
function findFirstChildNodeByName(childNodes, nodeName) {
for ( var i = 0; i < childNodes.length; i++) {
debug("childNodes[i].nodeName = " + childNodes[i].nodeName);
if (childNodes[i].nodeName == nodeName) {
return childNodes[i];
}
}
debug("no node found with name " + nodeName);
}
/**
* Finds a child node by its class attribute value. Simply walks through the
* child array and compares the content of the class attribute (if available.
*
* @param childNodes
* Array of nodes to search through
* @param className
* Name of the node to search
* @return node found or <code>null</code>
*/
function findFirstChildNodeByClass(childNodes, className) {
for ( var i = 0; i < childNodes.length; i++) {
if (!childNodes[i].attributes) {
debug("node with no attributes");
} else {
var nodeClass = childNodes[i].getAttribute("class");
debug("childNodes[i] class = " + nodeClass);
if (nodeClass == className) {
return childNodes[i];
}
}
}
debug("no node found with class " + className);
}