jjm2473 / GeocachingDownloadAsKML

// ==UserScript==
// @name         GeocachingDownloadAsKML
// @author       jjm2473
// @copyright    2016, jjm2473@qq.com
// @supportURL   mailto:jjm2473@qq.com
// @icon         https://www.geocaching.com/favicon.ico
// @namespace    https://openuserjs.org/users/jjm2473/
// @version      0.1
// @description  Download kml from Geocaching.com, with description and hint
// @match        https://www.geocaching.com/geocache/*
// @run-at       document-end
// ==/UserScript==
/* jshint -W097 */
'use strict';

// Your code here...
if (typeof unsafeWindow === "undefined"){
    window.unsafeWindow = window;
}

function XMLNode(tag){
    this.tagName=tag;
    this.childNodes=[];
    this.innerText=null;
}

XMLNode.prototype = {
    toString:function(indent){
        indent=indent||0;
        var cindent="";
        var i;
        for(i=0;i<indent;++i)cindent+='\t';
        //var nindent=cindent+'\t';
        var result="\n"+cindent+"<"+this.tagName+">";
        if(this.innerText)result+=this.innerText;
        ++indent;
        for(i=0;i<this.childNodes.length;++i){
            result+=this.childNodes[i].toString(indent);
        }
        if(i)result+=("\n"+cindent);
        result+=("</"+this.tagName+">");
        return result;
    },
    appendChild:function(node){
        this.childNodes.push(node);
        return this;
    },
    setText:function(text){
        this.innerText=text;
        return this;
    }
};
function getStructedGeoCachingData(window){
    var TypeName={
        "2":"Traditional",
        "9":"A.P.E",
        "5":"Letterbox",
        "3":"Multi-Cache",
        "6":"Event",
        "453":"Mega-Event",
        "7005":"Giga-Event",
        "13":"Cache In Trash Out",
        "1304":"GPS Adventures",
        "4":"Virtual",
        "11":"Webcam",
        "137":"EarthCache",
        "8":"Mystery",
        "1858":"Wherigo"
    };
    var id = document.getElementById('ctl00_ContentBody_CoordInfoLinkControl1_uxCoordInfoCode').innerText;
    var diffTerr = document.getElementById('ctl00_ContentBody_diffTerr').getElementsByTagName('img');
    var difficulty = diffTerr[0].alt.split(' ')[0];
    var terrain = diffTerr[1].alt.split(' ')[0];
    var boxSize = document.getElementById('ctl00_ContentBody_size').getElementsByTagName('img')[0].alt.split(' ')[1];
    var descHeader = document.getElementsByClassName('CacheDescriptionHeader')[0];
    var descHtml = '';
    var cd = descHeader;
    while(cd !== null){
        descHtml += cd.outerHTML;
        cd = cd.nextElementSibling;
    }
    var lat = window.mapLatLng.lat;
    var lng = window.mapLatLng.lng;
    var name = window.mapLatLng.name;
    var type = TypeName[window.mapLatLng.type];
    return {id:id,name:name,type:type,lat:lat,lng:lng,
            difficulty:difficulty,terrain:terrain,size:boxSize,desc:descHtml};
}
function getGeoCachingNode(cache){
    var pm=new XMLNode("Placemark")
    .appendChild(new XMLNode("name").setText(cache.name));
    
    var link='<a href="http://coord.info/'+cache.id+'">'+cache.id+'</a><br/>\n';
    
    var base='Type:'+cache.type+' Size:'+cache.size+'<br/>\n'+
        'Difficulty:'+cache.difficulty+' Terrain:'+cache.terrain+'<br/>\n';
    
    var style='<style>.DecryptionKeyWidget{font-family:mono;}</style>\n';
    
    var desc=new XMLNode("description")
    .setText('<![CDATA['+link+base+style+cache.desc+']]>');
    pm.appendChild(desc);

    pm.appendChild(new XMLNode("styleUrl").setText("#msn_grn-stars"));
    
    pm.appendChild(new XMLNode("Point")
                   .appendChild(new XMLNode("coordinates")
                                .setText(cache.lng+","+cache.lat+",0")));
    return pm;
}
function wrapKML(cache,node)
{
    var KMLHeader='<?xml version="1.0" encoding="UTF-8"?>\n'+
        '<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">';
    var KMLTail='\n</kml>';
    var doc=new XMLNode("Document").appendChild(new XMLNode("name").setText(cache.id+'_'+cache.name+".kml"))
    .setText('\n\t<atom:author><atom:name>export by jjm2473\'s script</atom:name></atom:author><atom:link href="https://openuserjs.org/scripts/jjm2473/%E5%85%AD%E5%8F%AA%E8%84%9AKML%E4%B8%8B%E8%BD%BD"></atom:link>\n'+
             '\t<Style id="sh_grn-stars"><IconStyle><scale>1.3</scale><Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-stars.png</href></Icon><hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/></IconStyle><LabelStyle><scale>0</scale></LabelStyle><ListStyle><ItemIcon><href>http://maps.google.com/mapfiles/kml/paddle/grn-stars-lv.png</href></ItemIcon></ListStyle></Style><StyleMap id="msn_grn-stars"><Pair><key>normal</key><styleUrl>#sn_grn-stars</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#sh_grn-stars</styleUrl></Pair></StyleMap><Style id="sn_grn-stars"><IconStyle><scale>1.1</scale><Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-stars.png</href></Icon><hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/></IconStyle><LabelStyle><scale>0</scale></LabelStyle><ListStyle><ItemIcon><href>http://maps.google.com/mapfiles/kml/paddle/grn-stars-lv.png</href></ItemIcon></ListStyle></Style>');
    doc.appendChild(node);
    return KMLHeader+doc.toString()+KMLTail;
}
function exportString2File( output , filename) {
    var blob = new Blob( [ output ], { type: 'text/plain' } );
    var objectURL = URL.createObjectURL( blob );
    var a=document.createElement('a');
    a.href=objectURL;
    a.download=filename||'Untitled';
    a.click();
}
function onClickListener(){
    var cache=getStructedGeoCachingData(unsafeWindow);
    exportString2File(wrapKML(cache, getGeoCachingNode(cache)),cache.id+"_"+cache.name+".kml");
}
function createDownloadLink(){
    var aa=document.createElement("a");
    aa.addEventListener("click",onClickListener);
    aa.href="javascript:void(0);";
    aa.innerHTML='&nbsp;Download&nbsp;KML&nbsp;';
    return aa;
}
function addDownloadLink(){
    var nav = document.getElementsByClassName('CacheDetailNavigation')[0];
    var ul = nav.getElementsByTagName('ul')[0];
    var li=document.createElement('li');
    li.appendChild(createDownloadLink());
    ul.insertBefore(li,ul.children[0]);
}
addDownloadLink();