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); }