Skip to content

Instantly share code, notes, and snippets.

@DieBatzen
Last active May 12, 2026 16:07
Show Gist options
  • Select an option

  • Save DieBatzen/b07694f0acfa70804627112adf2b2d75 to your computer and use it in GitHub Desktop.

Select an option

Save DieBatzen/b07694f0acfa70804627112adf2b2d75 to your computer and use it in GitHub Desktop.
GCTour-Testversion für den Wegpunkt-Export
// ==UserScript==
// @name GC Tour
// @namespace https://gist.github.com/DieBatzen/5814dc7368c1034470c8/
// @version 4.39
// @description Cachetour planning made easy. Pick some Caches, sort the list and print it out. Free for all users of geocaching.com!
// @author Die Batzen, madd.in
// @run-at document-end
// @match http*://www.geocaching.com/*
// @match https://www.gctour.de/map/show*
// @exclude /^https?://www\.geocaching\.com/(login|jobs|careers|promotions|blog|help)/
// @exclude /^https?://www\.geocaching\.com/live/(geocache|trackable)/[^/]+/log/
// @updateURL https://gist.github.com/DieBatzen/5814dc7368c1034470c8/raw/gctour.version.js
// @downloadURL https://gist.github.com/DieBatzen/5814dc7368c1034470c8/raw/gctour.user.js
// @supportURL https://www.geoclub.de/forum/t/gctour.78798/page-99
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_log
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
// @grant GM_listValues
// @grant unsafeWindow
// @icon https://www.gctour.de/i/icon.png
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js
// @require https://ajax.googleapis.com/ajax/libs/jqueryui/1.14.0/jquery-ui.min.js
// @require https://raw.githubusercontent.com/eligrey/FileSaver.js/v2.0.4/dist/FileSaver.min.js
// @require https://unpkg.com/leaflet@1.9.4/dist/leaflet.js
// @connect gctour.de
// @connect geocaching.com
// @connect gist.github.com
// @connect gist.githubusercontent.com
// @connect nominatim.openstreetmap.org
// @connect *
// ==/UserScript==
/*****************************************************************************
* Copyright (C) 2008 - 2014 Martin Georgi, as of 2015 Die Batzen
*
* This 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 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.
*
* To obtain a copy of the GNU General Public License, please see
* <https://www.gnu.org/licenses>
*****************************************************************************/
(function() { // begin of immediately-invoked function expression --> own "GCTour namespace"
"use strict";
/* globals */
let
VERSION = GM_info.script.version, // will be checked once a day
DEBUG_MODE = false,
GCTOUR_HOST = 'https://www.gctour.de/',
HTTP = window.location.protocol, // http or https
GS_HOST = HTTP + '//www.geocaching.com/',
GS_WEB_API = GS_HOST + 'api/proxy/web/v1/',
GS_WPT_IMAGE_PATH = 'images/wpttypes/sm/',
// set $ to jQuery local
$ = window.jQuery,
// are jQuery and UI loaded
IS_JQUERY = (
(typeof $ !== "undefined") && (typeof $ === "function") &&
(typeof $.fn === "object") && (typeof $.ui === "object")),
IS_GREASEMONKEY = (typeof GM_info.scriptHandler === "undefined"),
TOURS,
CURRENT_TOUR,
GS_USERINFO,
ROT13_ARRAY,
TIMEOUT,
STICKY = GM_getValue('sticky', false),
PROGRESS_BAR = {"progress" : 0, "total" : 0},
FAVSCORE = true,
DELETED_STYLESHEET_RULES = [],
SEND2CGEO = false,
AUTOTOUR_WEBAPI = true,
AUTOTOUR_MAX_CACHES = 1000, // 8000 is absolute maximum (by GS), but this slows browser down significantly!
LM, // Leaflet Map for autoTour
TOKEN,
WPT_ARRAY = [{
wptTypeId : "2",
guid : "32bc9333-5e52-4957-b0f6-5a2c8fc7b257",
name : "Traditional Cache",
shortname: "traditional",
gsDisplayName: "Traditional Cache"
}, {
wptTypeId : "3",
guid : "a5f6d0ad-d2f2-4011-8c14-940a9ebf3c74",
name : "Multi-cache",
shortname: "multi",
gsDisplayName: "Multi-Cache"
}, {
wptTypeId : "8",
guid : "40861821-1835-4e11-b666-8d41064d03fe",
name : "Unknown Cache",
shortname: "mystery",
gsDisplayName: "Mystery Cache"
}, {
wptTypeId : "5",
guid : "4bdd8fb2-d7bc-453f-a9c5-968563b15d24",
name : "Letterbox Hybrid",
shortname: "letterbox",
gsDisplayName: "Letterbox Hybrid"
}, {
wptTypeId : "11",
guid : "31d2ae3c-c358-4b5f-8dcd-2185bf472d3d",
name : "Webcam Cache",
shortname: "webcam",
gsDisplayName: "Webcam Cache"
}, {
wptTypeId : "4",
guid : "294d4360-ac86-4c83-84dd-8113ef678d7e",
name : "Virtual Cache",
shortname: "virtual",
gsDisplayName: "Virtual Cache"
}, {
wptTypeId : "1858",
guid : "0544fa55-772d-4e5c-96a9-36a51ebcf5c9",
name : "Wherigo Cache",
shortname: "wherigo",
gsDisplayName: "Wherigo Cache"
}, {
wptTypeId : "137",
guid : "c66f5cf3-9523-4549-b8dd-759cd2f18db8",
name : "Earthcache",
shortname: "earth",
gsDisplayName: "EarthCache"
}, {
wptTypeId : "6",
guid : "69eb8534-b718-4b35-ae3c-a856a55b0874",
name : "Event Cache", // includes all event cache types
shortname: "event",
gsDisplayName: "Event Cache"
}, {
wptTypeId : "13",
guid : "57150806-bc1a-42d6-9cf0-538d171a2d22",
name : "Cache In Trash Out Event",
shortname: "cito",
gsDisplayName: "Cache In Trash Out® Event Cache"
}, {
wptTypeId : "453",
guid : "69eb8535-b718-4b35-ae3c-a856a55b0874",
name : "Mega-Event Cache",
shortname: "mega",
gsDisplayName: "Mega-Event Cache"
}, {
wptTypeId : "7005",
guid : "51420629-5739-4945-8bdd-ccfd434c0ead",
name : "Giga-Event Cache",
shortname: "giga",
gsDisplayName: "Giga-Event Cache"
}, {
wptTypeId: "3653",
guid : "3ea6533d-bb52-42fe-b2d2-79a3424d4728",
name: "Community Celebration Event",
shortname: "celebration",
gsDisplayName: "Community Celebration Event"
}, {
wptTypeId: "3774",
guid : "af820035-787a-47af-b52b-becc8b0c0c88",
name: "Geocaching HQ Celebration",
shortname: "hq_celebration",
gsDisplayName: "Geocaching HQ Celebration"
}, {
wptTypeId: "4738",
guid : "bc2f3df2-1aab-4601-b2ff-b5091f6c02e3",
name: "Geocaching HQ Block Party",
shortname: "blockparty",
gsDisplayName: "Geocaching HQ Block Party"
}, {
wptTypeId: "3773",
guid : "416f2494-dc17-4b6a-9bab-1a29dd292d8c",
name: "Geocaching HQ",
shortname: "hq",
gsDisplayName: "Geocaching HQ"
}, {
wptTypeId: "1304",
guid : "72e69af2-7986-4990-afd9-bc16cbbb4ce3",
name: "GPS Adventures Exhibit",
shortname: "gpsa",
gsDisplayName: "GPS Adventures Exhibit" // to be checked
}, {
wptTypeId: "12",
guid : "8f6dd7bc-ff39-4997-bd2e-225a0d2adf9d",
name: "Locationless (Reverse) Cache",
shortname: "locationless",
gsDisplayName: "Locationless Cache"
}, {
wptTypeId: "9",
guid : "2555690d-b2bc-4b55-b5ac-0cb704c0b768",
name: "Project APE Cache",
shortname: "ape",
gsDisplayName: "Project A.P.E. Cache"
}
],
SIZES_ARRAY = [{
sizeTypeId : "micro",
name : "Micro",
newSearchId : 2
}, {
sizeTypeId : "small",
name : "Small",
newSearchId : 8
}, {
sizeTypeId : "regular",
name : "Regular",
newSearchId : 3
}, {
sizeTypeId : "large",
name : "Large",
newSearchId : 4
}, {
sizeTypeId : "other",
name : "Other",
newSearchId : 6
}, {
sizeTypeId : "not_chosen",
name : "Not chosen",
newSearchId : 1
}, {
sizeTypeId : "virtual",
name : "Virtual",
newSearchId : 5
}
],
// https://www.geocaching.com/about/icons.aspx
ATTRIBUTES_ARRAY = [
// Attribute: [GS id, image name, description]
['1', 'dogs', 'Dogs'],
['2', 'fee', 'Access/parking fee'],
['3', 'rappelling', 'Climbing gear required'],
['4', 'boat', 'Boat required'],
['5', 'scuba', 'Scuba gear required'],
['6', 'kids', 'Recommended for kids'],
['7', 'onehour', 'Takes less than an hour'],
['8', 'scenic', 'Scenic view'],
['9', 'hiking', 'Significant hike'],
['10', 'climbing', 'Difficult climb'],
['11', 'wading', 'May require wading'],
['12', 'swimming', 'May require swimming'],
['13', 'available', 'Available 24/7'],
['14', 'night', 'Recommended at night'],
['15', 'winter', 'Available in winter'],
['17', 'poisonoak', 'Poisonous plants'],
['18', 'dangerousanimals', 'Dangerous animals'],
['19', 'ticks', 'Ticks'],
['20', 'mine', 'Abandoned mine'],
['21', 'cliff', 'Cliffs/falling rocks'],
['22', 'hunting', 'Hunting area'],
['23', 'danger', 'Dangerous area'],
['24', 'wheelchair', 'Wheelchair accessible'],
['25', 'parking', 'Parking nearby'],
['26', 'public', 'Public transportation nearby'],
['27', 'water', 'Drinking water nearby'],
['28', 'restrooms', 'Public restrooms nearby'],
['29', 'phone', 'Telephone nearby'],
['30', 'picnic', 'Picnic tables nearby'],
['31', 'camping', 'Camping nearby'],
['32', 'bicycles', 'Bicycles'],
['33', 'motorcycles', 'Motorcycles'],
['34', 'quads', 'Quads'],
['35', 'jeeps', 'Off-road vehicles'],
['36', 'snowmobiles', 'Snowmobiles'],
['37', 'horses', 'Horses'],
['38', 'campfires', 'Campfires'],
['39', 'thorn', 'Thorns'],
['40', 'stealth', 'Stealth required'],
['41', 'stroller', 'Stroller accessible'],
['42', 'firstaid', 'Needs maintenance'],
['43', 'cow', 'Livestock nearby'],
['44', 'flashlight', 'Flashlight required'],
['45', 'landf', 'Lost and Found tour'],
['46', 'rv', 'Trucks/RVs'],
['47', 'field_puzzle', 'Field puzzle'],
['48', 'UV', 'UV light required'],
['49', 'snowshoes', 'May require snowshoes'],
['50', 'skiis', 'May require cross country skis'],
['51', 's-tool', 'Special tool required'],
['52', 'nightcache', 'Night cache'],
['53', 'parkngrab', 'Park and grab'],
['54', 'AbandonedBuilding', 'Abandoned structure'],
['55', 'hike_short', 'Short hike (less than 1km)'],
['56', 'hike_med', 'Medium hike (1 km-10 km)'],
['57', 'hike_long', 'Long hike (more than 10km)'],
['58', 'fuel', 'Fuel nearby'],
['59', 'food', 'Food nearby'],
['60', 'wirelessbeacon', 'Wireless beacon'],
['61', 'partnership', 'Partnership cache'],
['62', 'seasonal', 'Seasonal access'],
['63', 'touristOK', 'Recommended for tourists'],
['64', 'treeclimbing', 'Tree climbing required'],
['65', 'frontyard', 'Yard (private residence)'],
['66', 'teamwork', 'Teamwork cache'],
['67', 'geotour', 'GeoTour'],
['69', 'bonuscache', 'Bonus cache'],
['70', 'powertrail', 'Power trail'],
['71', 'challengecache', 'Challenge cache'],
['72', 'hqsolutionchecker', 'Geocaching.com solution checker']
];
/*
// TEST Anfang
// test3
jQuery(function($) {
alert("3. jQuery: " + $.fn.jquery);
});
// test2
if (typeof unsafeWindow.jQuery !== "undefined") {
unsafeWindow.jQuery(function($) {
alert("2. unsafeWindow.jQuery: " + $.fn.jquery);
});
} else {
alert("2. unsafeWindow.jQuery not available");
}
// test1
alert("1. $: " + $.fn.jquery);
// TEST ENDE
*/
/* Tampermonkey settings and functions */
// debug output functions
function toLog(typ, msg) {
if (DEBUG_MODE) {
if (console && console[typ]) {
console[typ]('GCTour: ' + msg);
} else {
GM_log(typ + ": " + msg.toString());
}
}
}
function log(msg) {
toLog("log", msg);
}
function debug(msg) {
toLog("debug", msg);
}
function warn(msg) {
toLog("warn", msg);
}
function error(msg) {
toLog("error", msg);
}
function info(msg) {
toLog("info", msg);
}
function log_table(msg) {
toLog("table", msg);
}
function log_exception(ex) {
toLog("exception", ex);
}
function log_timeStart(msg) {
toLog("time", "time for "+msg);
}
function log_timeEnd(msg) {
toLog("timeEnd", "time for "+msg);
}
// wrapper for properly alerting objects
function alertObject(obj) {
alert(JSON.stringify(obj, null, 4));
}
// wrapper functions for persistence
function saveValue(name, value) {
GM_setValue(name, JSON.stringify(value));
}
function loadValue(name, defaultValue) {
//debug("loadValue: '" + name + "', with default '" + defaultValue + "' (typeof " + (typeof defaultValue) + ")");
var result = GM_getValue(name, "");
//debug("loadValue: result -> '" + result.substr(0, 20) + "...'");
try {
return result != "" ? JSON.parse(result) : defaultValue;
} catch (e) { // fallback eval
debug("loadValue: FALLBACK :-(");
return eval(result);
}
}
// GM_xmlhttpRequest response info
function responseInfo(r) {
debug([ "",
"finalUrl: \t\t" + (r.finalUrl || "-"),
"status: \t\t" + (r.status || "-"),
"statusText: \t" + (r.statusText || "-"),
"readyState: \t" + (r.readyState || "-"),
"responseHeaders: " + (r.responseHeaders.replaceAll('\r\n',";") || "-"),
"responseText: \t" + (r.responseText || "-")
].join("\n"));
}
// init gct and dialogs
(function() {
var str = "";
str += "jQuery und UI geladen = " + IS_JQUERY;
if (IS_JQUERY) {
str += "\n\tjQuery Version = " + $.fn.jquery;
str += "\n\tjQueryUI Version = " + $.ui.version;
}
// str += "\n\tisunsafeWindow.jQuery = " + isjQueryWindow;
// str += "\n\tunsafeWindow.jQuery Version = " + ((isjQueryWindow) ? unsafeWindow.jQuery.fn.jquery : "");
//debug(str);
// init gctour object
$.gctour = $.gctour || {};
// init language object
$.gctour.i18n = $.gctour.i18n || {};
// set default Language
$.gctour.defaultLang = 'en';
// init current language = default language
$.gctour.currentLang = $.gctour.defaultLang;
// jquery ui dialog (default setting)
$.gctour.dialog = $.gctour.dialog || {};
// default dialogs (http://api.jqueryui.com/dialog/)
$.extend($.gctour.dialog, {
buttons: {
'OK': {
text: 'OK',
disabled: false,
click: function() {
$(this).dialog("close");
}
},
'Schliessen': {
text: 'Schliessen',
disabled: false,
icons: {
primary: 'ui-icon-closethick'
},
click: function() {
$(this).dialog("close");
}
},
'Abbrechen': {
text: 'Abbrechen',
disabled: false,
click: function() {
$(this).dialog("close");
}
}
},
/*
* Standard Optionen für ein Dialog
*/
basis: function() {
return ({
autoOpen: false,
resizable: true,
closeOnEscape: true,
modal: true,
closeText: $.gctour.lang('btn.Schliessen') || 'Schliessen',
show: 'drop', // blind, drop, scale
buttons: {
'Schliessen': this.buttons.Schliessen
},
width: 700,
height: 500,
minWidth: 300,
minHeight: 200,
maxWidth: 1000,
maxHeight: 700,
title: 'GCTour',
dialogClass: 'gct gct_dialog',
open: function() {
//$(".ui-dialog-titlebar-close").hide();
// $(this).dialog( "widget" ).find(".ui-dialog-titlebar-close").hide(); // x oben rechts ausblenden
//$(".ui-widget-overlay").wrap('<div class="gct"></div>'); // wrap für bessere Trennung zu gc.com
},
beforeClose: function() {
//if ( $(".ui-widget-overlay").parent().hasClass( "gct" ) ) {
// $(".ui-widget-overlay").unwrap();
//}
},
close: function() {
$(this).dialog("destroy"); // diesen Dialog killen, weil immer ein neuer erstellt wird
}
});
},
/*
* Info Optionen für ein Dialog
*/
info: function() {
return ({
autoOpen: true,
resizable: true,
closeOnEscape: true,
modal: true,
closeText: $.gctour.lang('general.close'),
show: true, // true, false, 'blind', 'drop', 'scale'
hide: true,
height: 'auto',
title: 'GCTour Info',
dialogClass: 'gct gct_dialog',
open: function() {
addJqUiTheme(GM_getValue('theme', 'smoothness'));
// remove interfering rules from GS stylesheet "coreCSS" (present on (old) dashboard page and others)
var sheet = getStyleSheet("coreCSS");
if(typeof sheet !== "undefined") {
DELETED_STYLESHEET_RULES = deleteStyleSheetRules(sheet,/.ui-|select/);
debug(DELETED_STYLESHEET_RULES.length + ' rules in stylesheet "coresCSS" temporarily removed');
}
},
close: function() {
// remove jqui theme
$("head link#gct-ui-theme-link").remove();
// restore deleted stylesheet rules (if necessary)
var sheet = getStyleSheet("coreCSS");
if(typeof sheet !== "undefined") {
for (var i=0; i<DELETED_STYLESHEET_RULES.length; i++) {
sheet.insertRule(DELETED_STYLESHEET_RULES[i], sheet.cssRules.length);
}
debug(DELETED_STYLESHEET_RULES.length + ' rules in stylesheet "coresCSS" restored');
DELETED_STYLESHEET_RULES = [];
}
}
});
}
});
$.fn.addShadowEffect = function() {
return this.each(function() {
$(this).on({
mouseenter : function() {
$(this).addClass("imgShadow");
},
mouseleave : function() {
$(this).removeClass("imgShadow");
}
});
});
};
$.fn.addOpacityEffect = function() {
return this.each(function() {
var $this = $(this);
$this
.css({
opacity : "0.5"
})
.on({
mouseenter : function() {
$this.stop().animate({
opacity : '1'
}, 300);
},
mouseleave : function() {
$this.stop().animate({
opacity : '0.5'
}, 300);
}
});
});
};
})();
/* images */
$.gctour.img = {
addToTour : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB9kFFAUXKNiDRngAAAJmSURBVDjLjZPPS1RRFMc%2F972n1qiUpqMRaj8UXPVj46Igw0WrKIKkwEWSCC4jjKLoX5BoU5GUJg6KFrVpFYiBEAouAkH0Of4endHRKSSY53v3tHgzbyxddFb3Xu753O%2F3nHMVwOBw5LpSvBQtFQIggpAJkV1lGCOe53a13Ln7lYNicKg%2FtrWdlIMinU7LenxNPn4e%2Ft0%2F8P7Jv7kGgOu6x48eKWEzmWA9ESMWX2FlbYml1XnimzF%2BplLcuHbzcEW48mlfpOfxPoAWQRBcz2XWnsWetbFtmzl7jpmZWaILUQzD4MrlplBB%2FqFn73q7G7MAC0C0BgHPc6mprvaBotGiEa1ZXl7hw6ch8q0Czp09H%2Fo%2BPtYJjAYArTUArreLbUcR0YgIon2QJ5q8vDzCleUopXB33atv3r6y2u91uFbWgg%2FQVFdXAeIDBH5cbMTZ2AQgXV5GQzzB6LcRU0RbgJuzkGnfQnQeUf4awNnY5FJbGyjFWHc3%2FoMa0cI%2BC0opTp05jVIqqPJGVqEEk4FoHajOAPyNaZoszi%2BSuHUbN7nlXwiFfIVKkVdYSMQwKAJTGcYiUGFlJfkAi9q6WmLJLRpaW4MXdUbRhebm4Gy8pyf8dxsVWIaJPb8AwK%2BpKf4nAgsKMAyTuro61sNhpicmAMgvKuJkfT0AC9PTODs7mRE0EmjNHgsK0zQBaJqeyhRS8aXkWFAwZ2eHFtE8f9HliUgN9x%2F6AMu0Vre2kydKS8vI1T%2B30ns6kEqlsCwznk476cCC4zjtA4OR147jVOV%2BsZ9UXlxMdHLSBxUX09vXs%2BJ5bkfng0cC8AdIoVh%2Ffv3rlAAAAABJRU5ErkJggg%3D%3D',
autoTour : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAFMAIwADJfKnZwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9kKEg8hB1Dip48AAAJvSURBVDjLjdNLSFVBHMfx73%2FOued4r14wpSLQNj0JokUtFIKCgqIXPQgikqSijUrqoqA21lIkywoiCAqyRUZRFBGFoIEkFdHGoPcLF75S79Nz7sy0UK6kIf03s5j%2FfGZ%2BzIwwR7UecLYrl91a0yOiuxpu82Vmj8wFXDzopPedeRrtf98V9r24lRsf%2BpkRpbpzob43tlK3NzVhBODK0ZILYSZRa612ZiI1NzMEqRGUDciO%2FmDwUw8f3z7na9%2BLz3U3s0tdgCAzXlfV%2Bk3F4vPwPD%2B%2F%2BFKVB0B65DvhyAeYGKE4HqNy23E%2BvutcAuACYI1y%2FRjZbIZsNjOdTzlYnaMgXoprFhEMjRIM90GYAGuZBgCbHsCEKbBmatKwbN0ueu%2BcpGxFJb5fgHKL8OML0clf%2BU3yAAMvMalB%2FPmriC1aizgRNh1upvPaETrfPCOdGsOGKUoXLGbL9j0UFcWBsWlATyTo%2F9DL4KtuhpPwO4gxmnWpPtTAhtIyQAhTAwTj%2FSCQTCZmRLCa4iKX4rjHcnEQ16Oh%2FjqvO9r%2FupXmlmpiC1fPjqBzOYJsCqNDrMlhTI6zp9ajdci5ll5On1iD0SFDX3ooceOzAQuIAjGKx9eeA7D12EYcNfnWnIiP47oYrXFcfzagRCGiUI6ws2YHVmseXX3CtuObAYh4UazRWNf8G0AEpSIYDA%2Fb7k03RKKTo1%2BItQZrNK4XnQ2ICCKKB2132duwHwvcb%2B3A8QsmT%2BAXIlNRI35sBiBickFaxcorOHS%2BgluNjfmGwrJKoIPC8sopQNBhGkRM%2FjdeqY5eCIOg1lrr8B8lIjrieZdrbmTq%2FwA8AAC7ufHXbAAAAABJRU5ErkJggg%3D%3D',
bg : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAoCAYAAAAPOoFWAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPZJREFUeNq81tsOgjAMANB2ov7/7ypaN7IlIwi9rGuT8QSc9EIDAsAznxvY4pXPKr05RUE5MEVB+TyWfCEl9LZApYopCmo9C4FKSMtYoI8Bwv79aQJU4l6hXXCZrQbokJEksxHo9KMOgc6w1atHXM8K9DVC7FQnJ0i8iK3QooGgbnyKgMDygBWyYFZoqx4qS27KqLZJjA1D0jK6QJcYEQEiWv9PGkTsbqxQ8oT+ZtZB6AkdsJnQDnMoHXHLGKOgDYuCWmYhEERCI5gaamW0bnHdA3k2ltlIN+2qKRyCND0bhqSYCyTB3CAOc4WusBEIpkeBuPgJMAAX8Hs1NfqHRgAAAABJRU5ErkJggg==',
bottomArrow : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAOCAMAAADKSsaaAAAAAXNSR0IArs4c6QAAAKtQTFRFHwAWEnMAFXUAIncAHXoDHnoHIHsAInwGI30AKH4TK4AAM4UGMYYLO4oQQ44VRI4XSZAbUZYhVpcnWpooYZ0wXZ45Yp4wW587ZJ4wXqBCZ6A0ZqA4YaJFaqI2baI4ZaRHZ6RFbaQ%2BbqQ8c6pOe65UgLJZf7JggbRmhLVlhLVpiLdqjbluj7tzlcB%2FlsGBl8GCncSHoMWJoMeJpMiMqMqPp8uPqcuQrM2Tr86VhHe%2ByAAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsRAAALEQF%2FZF%2BRAAAAB3RJTUUH2ggZCg4FgW6a6gAAAGBJREFUCNdjYGAQFBaTVWIAAQETcws5MIvPWNtcCsziNdQyEwHS7DyiBhqmKtKKDCySuppqOmaqEgwMnDJ66kbKQiB1rPL6CvxgdYxs4txcEHVMHMy41AEBUB0DFHCACADfrAlJwjTUvQAAAABJRU5ErkJggg%3D%3D',
closebutton : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAPCAYAAADphp8SAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAFPwAABT8BE2RkrAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAK9SURBVDiNfZNLaBNRFIb%2FO5lJJjGSNtO0lNAm0xZaWxeCpMG4KSrUirpxI0TduLIIPkAQBXEhSBe6EEHQhYoNWKuID%2BgLfODGUrCIGlsKtmBfE1MjQ5Kaedzjog%2FGRjzwcy%2Fn3P%2FjHC4HRASnBurrz77Zvv11L%2BDfWCMiDKrq5Vfbto30Al5nXoQjBiORi2oicV6yLL9LkkZSjHUmifS1%2BnBDw1U1Hj8l2rbXJYrDKca6kkR5AGBEtAKpq7sU2bHjvJDJeEEEV2UlFjVtbHZsbO9hy%2Fo5pKrXou3t3dA0GURwKQpmZ2bez42P704SFdc7Mg1DM02z6GbMS7YNvrSE6lAohlhsZCAS%2BRCNxY7y%2BXmPYwBulUpZAMZfHQHAs2DwcF0icd2Tz9eSaQIAXIEASJaJaxpbeyeEQvb3dPpRdmrqSJKIl4EA4GlFxb5wLHbLZ1n1vFRCWSjK77nPn%2B92TU93O9NlIADo93p3qx0d%2FeKvX5Vw1KWaGnwbHX1wYHHx2EaPsDGRYkyuiEYviLZdaXMOm2hdRi6HYFPTzsc%2BX8t%2FQSnGPFVNTS%2BU2tpdZi4HzvmKAHDOYS0vQxaEBqW5uf%2BhJFX%2FE5RizB1U1WdKOLzH1HVwInAikN9vmm53hgQBnAhWsYjNgcDWYGPj8xRjvjKQv6rqnhKNdhq6Dptz2JyDb9pkLE5M3F74%2BPF4SRA0DsDmHIauoyIcjvtDoftlIKNQ6NGz2UmIIjjnIJ%2BvpH39evPgwsLJQ7r%2B8kc6fc50uXKcCJAk6JnMpJHPX1mfzbkvfbLcOtzWln6XSBSeKEoPAAmAZ1XuvkDgzNt4XB9qafnSJ8utTu%2F69zPGPADk06K4pdnj2X%2BiULgHwL0KEwBwAOYNny%2F5yTCG7ljWBIASgN9EVHKCGABx1bwmz%2BopALAAmFhZCcNxN4mI%2FgBbEHoE%2FKbG8wAAAABJRU5ErkJggg%3D%3D',
copy : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAATdEVYdFRpdGxlAE9wdGljYWwgRHJpdmU%2BZ7oMAAABg0lEQVQ4jZWSy2oUQRSGv6qucaXuRFwqvorOmB7fIgR0J0iDZCUowoCCFxS84EJ8BS%2BTCT6NqCE7jWZyTtfvonvGMd2LSUFBURTfOf9XJ7x7%2F%2Fa%2B1V4pK0lCEsqZxTlLSHgqisnW5o1tjq9Xb16Y1liPnz60ZYGVncw9AXz78ZUQQrsBAgCSOH%2FuAkdmqVMdSG7eHFJiNp1BCLQEAIZXrwCweNcBmFsLGLBRbhBCJKwAJAFgZv2ABbkoCqafp00EYpNAMLo2agDuvHz9XMfFLjsoYkFZlhSxIMZICLH5hVy3EYytzZv%2FFX%2Fy7FGVVrN9%2FPCJGCMxBsbj681lG8fc2dv%2F3hGbFtlyzgxHwyXA3RBCWTBoOtjd%2FdIRm%2BqcHUhnTp%2FtlbSUCYzLsiM2hRgmt6tbVV3X6Z%2BgpvJiWGIMXLp4mZ2dWUdsZ7Ikce%2FBXUnSr4Of%2Bv3nQPP5ocyO5O4yM83nh5KkO9uVeqfrJGJ7AScR2wtYV2yds%2FcC1hEbAj4YnJr8Bf6RZNsaEpA%2FAAAAAElFTkSuQmCC',
danger : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIwSURBVDiNjdO9T5NRHMXx7%2B%2Fe573tU%2FoUCpTy1kjlTTExIRol0ZiY4OTE0iZuuhgHIxvRuGhcUAeMcXNwcwH%2FARcHjUpYTExUEsWQ6CAEWiiFXhc1QYvhLHc593OXc8UYQ6MsXJACyn4mZqdcM%2FXzRx%2Bbz416quFtQDnBk47j431to%2BdGHMt5uGev4eslORvrOTIUtmQk2ZpRXnbw1EJRRvcHiIhOJGda%2BoZ98SOUl6Tl4CFPfP%2FBvoC3RSYSPSNZ11WojiFUtp%2FAtyWWGxiYL8n4f4Hnp8Vy48l7UbY9EMtBpbKo5jxYFs1dXYH2%2FRluitoTaOrgYjzXFzpSRbUXmJyeZXJ6Ft3ej6drxNvymfmPFBsCby5JoILErSgdBOIGqGQLzaFNpslBRR2I45LOhDFl%2B9Mfroj7D6A2uNaUydh2vYLuHMRsb5BN2WQjG7Ozie4cxmWDMJ32yytc3gW8m5DIcrzJVIJAEhESC2G7QnezTb7Vhe0KEqYRSxM5qzED11%2BWJPwD1DxuJELXsswGunsQamWorjHWH3CsV8P6EpS%2FojsL2PU1wgDLF6YAZKFIzmje53Nu4OQK2IfP%2FB4E%2BuRdAHZeXAVjwNTZej3H1vIin5ap7BgOWGjuNMWxtami23upb64iokAUS09LgGCq64DBmDq6awDr2yKpOPrHGrctYCzuYQNsvZrbNZLUr7P693qAwMNdKXPCMoZHX74zZQxug97eETaV4b7s9Z33m5%2BP5JF%2FA6jokgAAAABJRU5ErkJggg%3D%3D',
del : 'data:image/gif;base64,R0lGODlhEgASANUlAJaWluXl5dfX197e3pSUlNnZ2aampre3t3p6eubm5qioqLW1taenp9zc3LOzs7a2toGBgdra2t3d3YuLi3x8fKSkpHV1dc%2FPz%2BHh4ZiYmH5%2BfpeXl4qKioyMjMXFxaKior%2B%2Fv8vLy9DQ0LS0tDs7OwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAASABIAAAZ4wJJwSCwaj8gjaZkcLp9QJOkhGHgACM2EQzJCv9EiaQSKBEQGAiHT9Y5IGFKBpCAB2uJ3gNQgOUgfeEQkCyR7EmN0gk4PJAl8iQyLQiQHjnKRkyWEhiQCfiQbmiQVJAMkFyQGJBSjCAAHISMAHRAWmptgTE28vUdBADs%3D',
dialogMask : 'data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%08%00%00%00%08%08%06%00%00%00%C4%0F%BE%8B%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0E%C4%00%00%0E%C4%01%95%2B%0E%1B%00%00%00%07tIME%07%DB%03%17%0C%03%0F%8C%CB%E4%8C%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00%26IDAT%18%D3c%F8%FF%FF%FF%FFMW%3E%FF%C7E3%FC%87%02%98%20%3A%9F%81%A0%09%B8t%C2%00%C3%20p%03%00%DA%B4%F2%A1%8A%CD%18%A3%00%00%00%00IEND%AEB%60%82',
downArrow : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAOCAMAAADKSsaaAAAAAXNSR0IArs4c6QAAAKhQTFRFEnMAFXUAIncAHXoDHnoHIHsAInwGI30AKH4TK4AAM4UGMYYLO4oQQ44VRI4XSZAbUZYhVpcnWpooYZ0wXZ45Yp4wW587ZJ4wXqBCZ6A0ZqA4YaJFaqI2baI4ZaRHZ6RFbaQ%2BbqQ8c6pOe65UgLJZf7JggbRmhLVlhLVpiLdqjbluj7tzlcB%2FlsGBl8GCncSHoMWJoMeJpMiMqMqPp8uPqcuQrM2Tr86VayLUTgAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsRAAALEQF%2FZF%2BRAAAAB3RJTUUH2ggZCiMw9%2FcEJgAAAFZJREFUCNdjYGAQEBKVUWQAAX5jM3NZMIvXSMtMEsziMdA0FQbSbNwi%2BuomylIKDMwSOhqq2qYq4gwMHNK6aoZKgiB1LHJ68nxgHQysYlwMUMDOQAIAAGnkBmRhpsy5AAAAAElFTkSuQmCC',
download : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9oIGQorAW7wjhQAAAMSSURBVDjLnZNNaFxlFIaf797v3tvMTOd%2FJjOZ%2FHSGhAyZmh%2FIlEhjSS00gihxoUatQqXqxoWFRkHBlVTcCi5ciBFLXQiiq0oRQw0qpoS0IaFiiyGJSabJOBPNZO783s%2BFaDBLHzibw%2Fs%2BiwNHcIjJ16dfAV7WpexTYCnH2VeKOU3Xrly9fO7jw3nxb%2FGNTwZNXfu6JxlrHenvIpkI4bIsiiWbe%2Bs7zC%2Bvc3dt%2B6YQ4sLVy%2BcW%2FyN46uJHWbe35aeJ0wNioCdKsWSzs7mG9EShJQDlHRKtIeaWN7g2u%2Fw7Qjz8j0QChEKeb86ezIgHUkEK%2BRzhzgy%2BcJxypU5u%2FVekU0Nr2pzJpmg6KnT9hzsfAg8CaC%2B%2B9ek7sWjAezrbjaE5NKQHNIEhJaYhcVmS1tBR4tEIEb%2BbbKadrrbgyLNvXjkPoHl9rgvDmU52i3nuV11E246hFDgKEBrCqbFV2OfLG0tcn53HZyn6utsAngSQe%2BV6pCsWYO7uNqmeKI46uGxpt8CJwQxCCJRSbGzdp2LbxCM%2BgGEAWak1NMsy0TxRdksVmo7CY%2Bk0m01qpR2ECANwa3wczeNh5aVXiXT0AgQBpK5rzh%2F7Fc1d2cQjLbb3w%2BQNk0bToVJoMFivc2t0FO%2FwMPbKCscSEbbtGkABQBqallv5Ld821B0j1RFndX2D2Xt%2FUsPErrn5dmSE9nSaowMDqGoV%2B%2BJrRAyDKdOMTE1MKH3ooSeSpWr9RKY7Qdjvxu%2Fz0hHQmVncou3tp0l1dhI4dYrq5ibuvj48x4%2F%2FPZkMpcVF9IXvvrjWnX3sktB10%2Bc2aDF1fpxfojb1Av3xOIGxMSqrqzTLZRrFIvVCgUaxiBEMsnf7NjpAsm%2FsztauPekgkVLj8%2B%2FX%2BaX%2FcZwbn9Fq27jTaZxKhSPt7UifDyMYBKXYW1g4%2BIVHz7%2F3TGeqY9rr85vpVIxY2ItlGSxNnuVkMol%2FdJTizAxr%2BTxHNA0JeKQ8EAD4g0lz%2FPlL07rlegRd%2BkEIpZzGma%2FelUOJBD%2Fncjy3tCT4P7zf26s%2BSKfV4f1fpUgaHTdq5X0AAAAASUVORK5CYII%3D',
downloadGPX : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9oIGQotDqgVNAMAAAG4SURBVDjLlZPPaxNBFMc%2Fs41CD1oMlVp1FUIPBhR6UxCPestucvIiqPtn%2BAOyET2I5yoUAmL%2FgMwS9OTBH%2FGgqHgT%2FAF1QTwYLG01yezsjKeN3TS25J3mveH74715I5rN5qM4ji8xQbiuSxAEAoAwDO2kEYahzcgK2aHb7WKMAUAIMVRzHCenXiwWc%2FmQwBhDu90eXnieRxRFw9z3%2FbHtOKOFSqWC53m7ArcRZLaFEDllKeWOBIXRQgbOCHdzkCPIrI8D15%2FU6Kl1lq%2B%2B%2F38LURQhhEBKmXsJgIFWHJ0tc%2FHBiZ0dSCnxfZ%2F6Yw9rNMpoVKo5fGCB8vxpNvq%2FedFbGk%2BwdZDaKM6fvExqDalJMVi%2Br8Wccs%2ByqXr8uv3Mvr2uRGErWEpJtVql1WrRR5Faw%2BrPTyRGo01CkiasDzZYdM%2Bxmfxh7WbH5vYgG1qtVqOv%2B%2BhUMzdzjEP7jzM%2FU2LP1DQH9x3hXdyh87nDl1v8czC6oj094OGreyij6OuE0myZM6ULvFl9ydOPz%2Fl2BwFQcF13pdFobPuNi1zJ5a0fdxHONK%2B%2FfqC%2BcH8pIGCiKN3Ya91rU3a0%2FheAk99ghKc72QAAAABJRU5ErkJggg%3D%3D',
edit : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAKQ2lDQ1BJQ0MgcHJvZmlsZQAAeNqdU3dYk%2FcWPt%2F3ZQ9WQtjwsZdsgQAiI6wIyBBZohCSAGGEEBJAxYWIClYUFRGcSFXEgtUKSJ2I4qAouGdBiohai1VcOO4f3Ke1fXrv7e371%2Fu855zn%2FM55zw%2BAERImkeaiagA5UoU8Otgfj09IxMm9gAIVSOAEIBDmy8JnBcUAAPADeXh%2BdLA%2F%2FAGvbwACAHDVLiQSx%2BH%2Fg7pQJlcAIJEA4CIS5wsBkFIAyC5UyBQAyBgAsFOzZAoAlAAAbHl8QiIAqg0A7PRJPgUA2KmT3BcA2KIcqQgAjQEAmShHJAJAuwBgVYFSLALAwgCgrEAiLgTArgGAWbYyRwKAvQUAdo5YkA9AYACAmUIszAAgOAIAQx4TzQMgTAOgMNK%2F4KlfcIW4SAEAwMuVzZdL0jMUuJXQGnfy8ODiIeLCbLFCYRcpEGYJ5CKcl5sjE0jnA0zODAAAGvnRwf44P5Dn5uTh5mbnbO%2F0xaL%2Ba%2FBvIj4h8d%2F%2BvIwCBAAQTs%2Fv2l%2Fl5dYDcMcBsHW%2Fa6lbANpWAGjf%2BV0z2wmgWgrQevmLeTj8QB6eoVDIPB0cCgsL7SViob0w44s%2B%2FzPhb%2BCLfvb8QB7%2B23rwAHGaQJmtwKOD%2FXFhbnauUo7nywRCMW735yP%2Bx4V%2F%2FY4p0eI0sVwsFYrxWIm4UCJNx3m5UpFEIcmV4hLpfzLxH5b9CZN3DQCshk%2FATrYHtctswH7uAQKLDljSdgBAfvMtjBoLkQAQZzQyefcAAJO%2F%2BY9AKwEAzZek4wAAvOgYXKiUF0zGCAAARKCBKrBBBwzBFKzADpzBHbzAFwJhBkRADCTAPBBCBuSAHAqhGJZBGVTAOtgEtbADGqARmuEQtMExOA3n4BJcgetwFwZgGJ7CGLyGCQRByAgTYSE6iBFijtgizggXmY4EImFINJKApCDpiBRRIsXIcqQCqUJqkV1II%2FItchQ5jVxA%2BpDbyCAyivyKvEcxlIGyUQPUAnVAuagfGorGoHPRdDQPXYCWomvRGrQePYC2oqfRS%2Bh1dAB9io5jgNExDmaM2WFcjIdFYIlYGibHFmPlWDVWjzVjHVg3dhUbwJ5h7wgkAouAE%2BwIXoQQwmyCkJBHWExYQ6gl7CO0EroIVwmDhDHCJyKTqE%2B0JXoS%2BcR4YjqxkFhGrCbuIR4hniVeJw4TX5NIJA7JkuROCiElkDJJC0lrSNtILaRTpD7SEGmcTCbrkG3J3uQIsoCsIJeRt5APkE%2BS%2B8nD5LcUOsWI4kwJoiRSpJQSSjVlP%2BUEpZ8yQpmgqlHNqZ7UCKqIOp9aSW2gdlAvU4epEzR1miXNmxZDy6Qto9XQmmlnafdoL%2Bl0ugndgx5Fl9CX0mvoB%2Bnn6YP0dwwNhg2Dx0hiKBlrGXsZpxi3GS%2BZTKYF05eZyFQw1zIbmWeYD5hvVVgq9ip8FZHKEpU6lVaVfpXnqlRVc1U%2F1XmqC1SrVQ%2BrXlZ9pkZVs1DjqQnUFqvVqR1Vu6k2rs5Sd1KPUM9RX6O%2BX%2F2C%2BmMNsoaFRqCGSKNUY7fGGY0hFsYyZfFYQtZyVgPrLGuYTWJbsvnsTHYF%2Bxt2L3tMU0NzqmasZpFmneZxzQEOxrHg8DnZnErOIc4NznstAy0%2FLbHWaq1mrX6tN9p62r7aYu1y7Rbt69rvdXCdQJ0snfU6bTr3dQm6NrpRuoW623XP6j7TY%2Bt56Qn1yvUO6d3RR%2FVt9KP1F%2Brv1u%2FRHzcwNAg2kBlsMThj8MyQY%2BhrmGm40fCE4agRy2i6kcRoo9FJoye4Ju6HZ%2BM1eBc%2BZqxvHGKsNN5l3Gs8YWJpMtukxKTF5L4pzZRrmma60bTTdMzMyCzcrNisyeyOOdWca55hvtm82%2FyNhaVFnMVKizaLx5balnzLBZZNlvesmFY%2BVnlW9VbXrEnWXOss623WV2xQG1ebDJs6m8u2qK2brcR2m23fFOIUjynSKfVTbtox7PzsCuya7AbtOfZh9iX2bfbPHcwcEh3WO3Q7fHJ0dcx2bHC866ThNMOpxKnD6VdnG2ehc53zNRemS5DLEpd2lxdTbaeKp26fesuV5RruutK10%2FWjm7ub3K3ZbdTdzD3Ffav7TS6bG8ldwz3vQfTw91jicczjnaebp8LzkOcvXnZeWV77vR5Ps5wmntYwbcjbxFvgvct7YDo%2BPWX6zukDPsY%2BAp96n4e%2Bpr4i3z2%2BI37Wfpl%2BB%2Fye%2Bzv6y%2F2P%2BL%2FhefIW8U4FYAHBAeUBvYEagbMDawMfBJkEpQc1BY0FuwYvDD4VQgwJDVkfcpNvwBfyG%2FljM9xnLJrRFcoInRVaG%2FowzCZMHtYRjobPCN8Qfm%2Bm%2BUzpzLYIiOBHbIi4H2kZmRf5fRQpKjKqLupRtFN0cXT3LNas5Fn7Z72O8Y%2BpjLk722q2cnZnrGpsUmxj7Ju4gLiquIF4h%2FhF8ZcSdBMkCe2J5MTYxD2J43MC52yaM5zkmlSWdGOu5dyiuRfm6c7Lnnc8WTVZkHw4hZgSl7I%2F5YMgQlAvGE%2Flp25NHRPyhJuFT0W%2Boo2iUbG3uEo8kuadVpX2ON07fUP6aIZPRnXGMwlPUit5kRmSuSPzTVZE1t6sz9lx2S05lJyUnKNSDWmWtCvXMLcot09mKyuTDeR55m3KG5OHyvfkI%2Flz89sVbIVM0aO0Uq5QDhZML6greFsYW3i4SL1IWtQz32b%2B6vkjC4IWfL2QsFC4sLPYuHhZ8eAiv0W7FiOLUxd3LjFdUrpkeGnw0n3LaMuylv1Q4lhSVfJqedzyjlKD0qWlQyuCVzSVqZTJy26u9Fq5YxVhlWRV72qX1VtWfyoXlV%2BscKyorviwRrjm4ldOX9V89Xlt2treSrfK7etI66Trbqz3Wb%2BvSr1qQdXQhvANrRvxjeUbX21K3nShemr1js20zcrNAzVhNe1bzLas2%2FKhNqP2ep1%2FXctW%2Fa2rt77ZJtrWv913e%2FMOgx0VO97vlOy8tSt4V2u9RX31btLugt2PGmIbur%2Fmft24R3dPxZ6Pe6V7B%2FZF7%2BtqdG9s3K%2B%2Fv7IJbVI2jR5IOnDlm4Bv2pvtmne1cFoqDsJB5cEn36Z8e%2BNQ6KHOw9zDzd%2BZf7f1COtIeSvSOr91rC2jbaA9ob3v6IyjnR1eHUe%2Bt%2F9%2B7zHjY3XHNY9XnqCdKD3x%2BeSCk%2BOnZKeenU4%2FPdSZ3Hn3TPyZa11RXb1nQ8%2BePxd07ky3X%2FfJ897nj13wvHD0Ivdi2yW3S609rj1HfnD94UivW2%2FrZffL7Vc8rnT0Tes70e%2FTf%2FpqwNVz1%2FjXLl2feb3vxuwbt24m3Ry4Jbr1%2BHb27Rd3Cu5M3F16j3iv%2FL7a%2FeoH%2Bg%2Fqf7T%2BsWXAbeD4YMBgz8NZD%2B8OCYee%2FpT%2F04fh0kfMR9UjRiONj50fHxsNGr3yZM6T4aeypxPPyn5W%2F3nrc6vn3%2F3i%2B0vPWPzY8Av5i8%2B%2Frnmp83Lvq6mvOscjxx%2B8znk98ab8rc7bfe%2B477rfx70fmSj8QP5Q89H6Y8en0E%2F3Pud8%2Fvwv94Tz%2B4A5JREAAAAGYktHRADVAJ8AvxXHGoYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfZBwcIASdkENZ8AAABgUlEQVQ4y5VTvUtCURT%2FmUmSWCqaSUtbEA1R4GRfFARBNfV%2FRNYUjY1JTY0N4VAiDdEgSNIQTUVDtgZBiPmBVkrq8%2FwaHj6VZ6%2B8cOGcc%2Fl9nHPvBbpckeAWW%2FOebsHjE6ttJKZGcH4WphHYVn3EqLKi5c9Pl9g4CJk0MEmKkPV6nYqisFarslqpsFL55tXpDpXXMJMnCSZPEmx10NvUINLplE75IX6I5blJpLNFjC0CF0f3CGwHgYNQO4EI4fEMgUKQ6o5H9jSw1z2I2M0j6J9GxyEKBZnMO7K5DHL5bEdwYH1X57B5C0K4XG44nS7V9oK%2FHby2C4p%2Bzs0WSOTzWe0g%2FZ6B1zuM2PUdppY2US6XYO2zGhCIwOFwQkhEo7dqMfmiKpMgBWLkABQUigUAwP5xRCuXyyUttlgsvxOQhN0%2BoN0AKZBGLKLVDVogPr8%2BDJ%2By2Ww2ciCw9dtUVaiqQgKi5oqigCI6gn%2F%2FhcaamZ2Hzzdi0hEAQCr19idJKxgAfgDG6PPJecMc5gAAAABJRU5ErkJggg%3D%3D',
gctourLogo : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIcAAAAYCAYAAADQ1%2B6cAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oIGQo6LQ%2FxwecAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAEBBJREFUaN7tW2tUVFeW%2Fu6jHlRB8S5eIo8SRFAQFaKxfSW2qBgjMmpixkfSJplMllkaku6OkzG2vZJxTDR20tMmrCahk8YsO3REDGjHFyrYohGiRsQSQZRnBSwKq6iqW3Xrzo%2Bcq8ebinavNT8GV%2B5aZ91bt86rzv7O3t%2Fe%2BxQDckmSxODOxQDgAKgAqAFoAGjJsxoAT75nSX0RgADABcBJ7m7yTgQgMQwjUf0rx%2FvBpax%2Fvzb%2FF%2F3%2FdCnWSLGQDBE4RwCgAaADoCclgAKJDBCGAscQADuAQQC3yGc3AK8MEMVY9P22XBX3H8z3n2hzL4BIPwHl3hdPhEWDQgaGmoBBDyDw1JtP5vdfOvVzdVConeVVrMdhCx%2F6riOJ5VWCPia5ZcTUxX9Pe%2BLX5xiOsxFAqUi%2FPrlIkkQDkC4MJTAfuYvkLinaMD%2FSRllfWY8GhUTNS5IkifkJIH52oiRJLAGDijIbKlI0AHRfPZe5Xh0UJjy0YdeRgPBY2cxwouDmzH%2Fdnta2v%2BQRR%2FfV0dqw6I7Uf3nls9FLik4D6AXQA%2BAmAAfRHrLAeGoc2jzJQvaQ%2Bl5K2Dw1T568k4Xspdr4KLOoNH%2ByCaTHEClA%2FlMmyJ%2FpepBAxkiSpFKYDy0lNO7o%2BmlLw9Ieupb1%2FDtXARiuH%2F1s9K0Oc0zGijeayOICAH%2F1yw%2FGXCp783FnX0ecccLsAzP%2B%2B%2BBOAJ0EJINEGBylkWgTxVGC8xDO4qI4C0fxHnp%2BEqnvJlzHTc1JBrGGApNP0b%2BTfJYB4s80SfcyyT9mrh4EkLBkoQMABAOIxMUDY%2FDJLxYCCDOXb09X6YOdWc%2B%2Fc44somegpTH86t7%2FWUgtsgOA07Tg38x5HzUVB8aOumZpODSv%2FXDZGACBFABkAIYAiAQQDSAOQJwgCPGCIMSTz7EAYgAYAYSTEgkginwXByDO5XLFezweum4kgFAABqqEkfcxVL9yvSAyHy0FIo0f7UlzK4bSShylyeh6HNl0zP1I8f97zkHKHaH9ZX0RHl1XBSDkRs3uvFm%2Fqy0mu8ELwGta%2BGLTlYr3g31eQWJ5tUjeswB8qoAgccrG8orrRz%2BLGzGtUOYeOgIiiTyHAQi5fPlyfHl5eUFHR8ckl8tlBACtVmuJi4trWLRo0d6xY8feIO188hzPnj2buG%2FfvkU9PT3j3W53KACfXq%2B%2FkZCQcGzlypXlUVFRfYQE%2BwCo1q5dWyyKYgTDMCxD2LAkST6DwdC4ZcuWN8i8XRRf8fm5eynzIykIuz%2BT6KW0kTTcwSFrDgOOf5iNXnMucp7cfe3gJxkBEXF2llNpyI%2FkAHD6qAQh1JTd0Fm7Jz5%2B5rI2SvvwADjDyDGu2MkLer67cDw%2BeuKcfmKqPKQPA4DwL7744mfV1dXrQkND22bNmrU7MzOzjed578WLF0eePn16xo4dO3bk5eX9YcmSJV%2BTdvyuXbsePnz48Jro6Ohz%2Bfn5Jenp6ddcLhfT0NCQfObMmQWbNm16dNWqVa9Onjy5lZgjdvr06R%2FxPB%2Fe39%2BfXFdXt2rJkiXveDyeIZ1O1000h1pBZH2KQpssgbzjKG0je23y5nETsDGySZQkadiaGP4ul%2FXUp7MQZLQgwKC9fmTX1JRFaw8S0yDzAh0AbfrKN05c%2B1tpZvzMZRar%2BWxga3Vx5kDr%2BVRHT9tIwdYXLvlEVmcc2Z5f1n6FtPcQEIXU1dWNq6qqenXChAl7XnjhhS9ZlnWQhZRGjRrV8fjjjzcUFxfn7d%2B%2Ff21iYuKbOTk5lhMnTiQfOnTouenTp5etXr36IHGX3QDEjIyMK0uXLj20cePG9aWlpb9NT09fYTAYvADEJUuWNAKIqK2tZevq6jB%2F%2FvwW0tZOwBFImQp%2F3pKL1LUR8%2Bkja6GnzJKa1HcR932Q0iK%2B4aw9%2BLtsZ0%2FzGESP7gagGeptT4jOmSsQM%2BDFd616BEcz8Lj0hoQMceBq45ivnh9v4hhIQRExAyHJmZbIzBm9nCYAnErraan8fa6rtzVCG5U8SHkqQRUVFStHjBhx9sUXX6wiiz5IdqYkg%2FS5556rioiI6Jd3aWVl5eLk5OT61atXHwZgJUUWFqvRaFQvvfTS5urq6rTu7m6vwWAYojiVSxRFjyK4FwBAe%2F78eWN5eXlhb2%2FvWFEUA%2FR6fU9GRsaxZ5555jjP8%2BK6deteXbx48dvTp0%2F%2FhjIxAVu3bl1qt9uzNm%2FevPvll1%2FePGXKlL0nT57MY1m2e9u2bS%2BT38PeJ84yLMBxJ%2FbAMAxC4gYBqCXRq2VYLhCA1nNqV8zA0Y8noevbUb1OlrkZnnEjMHbUTXVQ2FBuSswtXK4ZhcK3ziM%2B2wmAh3OQMx77r1nMe3OfxZvm3xBhMC0tLcb%2B%2Fv6MwsLC31C7cpDiJLdV9%2BLFixsAGNra2mL6%2BvqSly9f%2FikBhBVAH3mWPRlVXFwc9%2Byzz3bKxJkyd4IkSSIFFjUArra2Nqm0tLQoJibmXEFBQXFERMTghQsXRtfX1y%2Fu6uoat3HjxjKbzZYhCEIExZtYABqXyzXSbrenAgixWq1jDh48ODI%2BPv7o%2BPHj9yi00LDnHHfsLcP60NU0AoBKkny85PMFXvrsrdTmXW%2FlZxlc2oRglSd01ppj%2FNJt57w%2BH75akfiMqMu9yvW1xmLQ8r35%2BNMzD6G%2BLCfMJ%2FBuZ4CDsstobm5OYFnWnZub26Egej6K1NGBLO7KlSsxHMcJWVlZXZR35FCAg1e4w%2FJ7D7H7Eg0OURSZ3bt3%2F8JkMp167bXXPpWJ76RJkzpyc3Ovvvvuu69XVlZOBACO4%2BiUAQCoOY7jWZZlCegxevTo40VFRX8C0PWPxk6GCzjk2IKAxJyz%2BPbAPBzaYdKEGG%2FdNJ%2BJDEudZA9OzmwPmfpIG5%2B%2FzowvXpuA10et4tdW7dHGploua9MG0vlvRGTk2WC9ocPFA2kQBR4AOMGuozSTJIoiyzCM%2BP26fi%2F87du3z7NarZEMwzAsy3JqtVqj0WgC1Gp1gEqlCtDpdD6GYXwsyzL3iT3cKxIqXxwAtr6%2BPt5ut8esWrVqGwHGIFmHgIyMjE6TyXSmoaFhPACwLMtT7i0LQMNxHM8wjKyZkJWVdYEioy6ynsMeHCwVr7BjxR8roAvpx%2BcvF07RdIYO%2FnVTTnTOXOuj75%2FaH%2F7EW5cQZBTx5O8b4HFq0N8eFJY6qaf73IkkMCxQ91EizlfFwtYdKnfuSp19ilLzYnJycocoirrm5mYjYfwBLpdrpMvlSnU6nSlOpzPF4XCYbDZbQk9PT1p9ff2CiIgIh9fr1VJtdFSeR0dIpRzXCCLfqanYhBJAbHd3d7hGo7kVGxt76%2FbGoJKFRqOx69atW6FEcyiDhDqO41QEHCwAqNXqIQocbipSO6yDYTxZHAcAG4Kju%2FHGhY344%2FJ%2F1ZmPTU0avBEl%2FWpELDPz389gzi9bwPES9r0xFiwvImthX6wUJF0%2Fsisbr2w7is%2BLZsH%2BnUHuuMWh8iQ99d4hatHYcePGdQUHB7fu27dvRlpa2ucAxA0bNuyXXU8ihEAAhp07dy6w2%2B1ReXl55gMHDli%2B%2FPLLmWlpaddJAEsi4PDJYX5RFPn6%2BvqwpKSkzpiYmH7SJ6PQND4ACAkJuSUIgs5ut6sDAwNld17WLIzdbg%2FSaDRDLMv6vF5vAJmT7N7qJEkKIuDgSRjdR8YTFDERPAiaY4gQPQuCY9pRdPRDbLq4YcA0p85t6zOg4j%2Fm49cjVuDzoiycKH4IQUY7vt1vNPIubnxach%2F2vv4zGhj9YoD7Zubyo1x4vI0wd6fME2bPnl126dKlR%2Fbu3ZtNFj2YCDyEPOtrampSvv7669lz5szZx3Gcffbs2dVNTU0zKysrJxHvKUoR9YzasmXL2tLS0hKn0xlMhdcZkliTBeUFIEyePLmF53nX7t27pxCQBZGxAx0Oh85sNk80mUzNer2%2Bv7W1NYHMM5REa0MsFstowjk4Ag7RD9eQhnsIXeYcTmqHfR%2F4iRnjDvnl3%2F58%2FsOiHt%2BpP8%2FKFFwa9tD2mQAAp02P9%2BcvBYCRfkx%2Fv6h2T3h6099h7%2FciMNxFwCcBQH5%2B%2Fpne3t6SysrKZ5ubm2see%2ByxI%2Bnp6X0A0NnZadizZ8%2F0xsbGednZ2ZX5%2BfknAYgLFy480d7ebqioqHjBbDYnFxQUfGUymfoBSGfOnImtqKhY1NvbO76goGBDcnKyg%2BIbIgCRcmWdALx6vZ6ZNm1aWU1NzdMMw4hPPPFEnU6nE8xmc3hJSckKhmHEZcuW1bjdbpw%2BfXpuWlpa28MPP9w6NDTEl5SUzLVarUlGo7FNzuOQ%2FkVKuzwwiTc6gynb9GAq%2FxHVWlU88fIn%2F7lwaoSgMngHAv32xKm8Xaq474asluCECTMaVH3mcCTmHsGaXWXE9fSQ%2FkMBhB4%2FfnxsdXX1UovFksXzvIthGFEQBENwcHDrjBkzygsKCk7LwpTD53v37p147NixQqvVmqJSqQYlSWJFUdQZjcazhYWFH%2BXk5JgB9BMt6CZmKqyqqmpyeXn5Bx9%2F%2FHEBlenVlJWVTa2trX3K7XaHqVQqhyAIQdHR0Y1r1qwpNZlMNrfbrX777beXt7a2zuJ53i6KoiYkJKQ9Kiqqra%2BvL37r1q3vPf30039YtmzZS3Pnzq0FYCGxGzcA33DXHHRyiKWipYEAIm4nutyOGC%2BYoBvFL05N%2BrZ0qoUNHdRqtG6OESFIvKdP4MTrNi%2BrMU26NNkUfZO%2FejwNLGfDUx%2B8h5RpZkpYHCF1MoHUWa1W%2FYULF2IFQeBTUlIsCQkJVsoMDZEdyRP1HwhA29nZabh8%2BXI0y7K%2BjIyMrsjISBupayPFTkCgIeOEEGDKsRRJzg6LoqhtbGyMs1qtgSkpKT2JiYlWilByAFSdnZ2GpqammLCwMEd2draFZVmaw7jImP1UcE54EMyK8iSY7J7pAYRD9Mah69tUHP%2FwETy18yKGBrQoinxF2GrZ1NtwKMjrsnsCwmJtoaMn9WsM4e7bUcShAREttTwyF7SSBRukhKL1k7JnFSl1OgVPp%2BzlNjxpI7uqAqk%2FhDunz2SyqiPt1FSUU6L6VKb0vRQ4WCrFwCpC7LQZdhJAOqg5PxjgoADCULs0FDe%2BScJf1q%2FB1ZMFSJh4FppAJ9x2FX51skSxGN57JKwclO8v4e6TZjwlGIbiCbL7qxSSSkk2cfcBIQ91BgSKsVhFck1Or6sU4KRPldEn5FiKy9BCF6lx5TlLD8J5Dv42Sr7PaNOpZwHx4%2FuQMLEeEUmdMI4axLwNTZTA7WSXCpQQ7yKClMA8ioSWl3JfWT%2FBK3%2FH%2FgRKSMr4hU8hWCiA4KXA9GNHD3%2Fg8lIbiFGMKfmZr%2B9BcWH9RhWJ9mApdRxEipYIRs5UOnH3ySufQtX6S4Hf64CxUjjSPaKgyvYS7n0oGfdJgNH9%2BevH33jwA5QH5gTYDzSH4seKVDLMTal%2B%2BjCLQGkI6Ud21A8WTb7fCT34F%2BiP%2FDXhXgC411lPv3UIUO8nzH9I2A%2FiAeX%2FBUPhQRLDQ08NAAAAAElFTkSuQmCC',
gctourLogoSmall : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAYAAADwMZRfAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDGQg7CZXhIq0AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAD30lEQVQ4yy3M2W8UBRwH8O9vjj2ms92rpUsPutClLdjVQi0WS%2BHBNCq2QCKJhoemLxhr9QlTEhIjPIgxKiRINKSiQEKEBINIsVpSSRpQMLZKCU3tfQBdeyzda3Z3ZnZ%2Bvvj5Az5kWZaLiOwAFAAeZl6z%2BNevNRM%2FnGoB4JEUl5ReeuRNRaY9joKSZFnT%2Fpny5rYRu7tgEsA%2FAMaImSsBuP5P8mb7L25%2BfPvqzuo3D8%2F5qupVQ0sUyoqLLNPgse9Pls%2FevFCua3E5tOedBxWtHX2ykn9HAlDFM39uIFESkjZveuH367X1Xed%2FkZ15%2Fid3e8omrp1%2Brul47z1BklPVb3TFK%2FZ0LE799HVo%2BMz729jQ9eoDR2YkTixtxc3PX%2BP2s7cfnuiorDvUfUNyKGlmThc3tDy5d%2FzAbiLKAkgBEGWnyxXa%2B%2B5TpaAUeYGgJYhSoUBDVxqxMh2MR6MVzoISu%2Bx0qUTkJCKZmaXSptdnouNDfmZWcnpG1RNR1UhEHe4NYcHQ4r6ckQ2Q9UnjAoVeNEesYi0QbpzyVdWPA0gDUAEEsvGVwgfdh4Nrt7dGVifv58dnR%2FypyJRLj6%2BIejrFzWeGr0mIRVQE65%2Fqg4MeW76%2FAIDAc0OO5G%2BXS1aXI66Et9LKrC45kyMDtuJsxFa2721NKgpZDn%2BxOH%2BqzS5c%2F2CrBLaAlVkHBMkm2hyF030XSsa%2F6ija5taENTvbk4H9h%2BKr44P68qUjHq8tKiDPyXD7BZxvz1s3elk2fEESEN49hzvfuFQzJqXmR92e0BZ74KX2jHpiftn27KuWfLS6SFFdtmTx85wmhVASlhAZdfAfl2RiE5RNigL2fnSLFa9W8fCcU%2FiuU%2FUaS1K483RW8pWKqN1ngS2SVa%2FIVk7U3OsIfZ8pfOsLG1kGMjnAbO6akEhx3%2Bf3eon%2BvvqCMnCu0jz5ikuq3GFy01smhm%2FI8Ach%2B0ul4vUbybaQIP75Y5GMDHIWMO8OZ8sb26eImRuYOQSgiixz8%2FjFYw3ywJeBYD4IWpwgiIBdZTZ1gpEBLBNEQEwHzLZv53y72vqJmQPMXARgIxHVAtgy1tNdZ%2FR%2BWrCJZ0XkTBAIAMMCYAl2XvDWZO2ymCtSZY2D9UPEzA4AbgBBAHVs6ttJT4ZTpuB3HK1aG9vUkhB0zQDYyNjcmccJy%2FLW7Fhab08amLwro%2FXYFQmAwcwZIkrC1OP48cMAj%2FSVKU0Hl1BYHvMdPNuvJVZXQMKKR81PBACdmU0iymJXZ4yZ%2F5WYmYnIYGYNorwI1TeGZ142UBKOoLMnAuCRU3VHAESZWQNgEpHFzCYAnYhS%2FwF8odAV4EB4aAAAAABJRU5ErkJggg%3D%3D',
globe : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9oLCAk1NEFBgZsAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAC40lEQVQoz12ST2hbdQDHP7%2F3Xt5rmixpmj9Nm7VdQ8vGimsrqyieVNTJULwoFbrDxnAguwjDHbxKQRTvO4gVpSDiPMlEvOhgamctK%2B1WtqyzaZtmeVn%2B5yXvveT9dhDn5vf8%2BXwPX76C%2F2Xug8VzwLuqph2VYEjPa0rJsqIqXy8tzH%2FxOCseSRe%2FnNZV5ceJseTAs8dGGUtF6TUMyo0WmR2TlY0d7mQL14UQZ5cW5tceyW%2B%2F%2F%2FlsIOT%2F480XpsTURIJyo4WZy6IFE%2BCPgGWSGoiyvLHHlasbDxDixaWF%2BTUNIBoN%2FvzK85PiqXQ%2FpWKe2Mgk4dggVtslv7OF5jko3RYvzabpejL607Vbl4Dn1DMffvXRwVTs5bkTM3Rsi5qrEgxHUBUFKcG1qsTCPaQGk4SCfnp6fGTzlYMj069ltVC49%2BzxyREq5SJFJ0BiaAgpQQIIBeE57Jc8VjLrRPySqSOHODo%2BxHau9JZWt9z4aDLC8p0C6YkEnvxvxUalxDPTkwghkFKyt3%2BfdqvFYDwMcFxpOx3FMHSUYIJKo41Zd7DsLnXLwWmYCPFPlRCCgXiUtc27GD4NoF9TVcWrNttKoJ0jqBkUmjGKPp1O16Nd6jDtuti2w4FgAMuyOJSKU2g5ACXNpyj5e7vFoZnxJOnhQbZ39riaqeGg03ICfPrdDQzR5b03jlGv11m9eRcOjAL8qchu9%2FLq5i6OpwEwOpzi5Eyc%2FUKJB9UmVVuwXfYwTZNqtUpPJMXmVh7gW3X118tXxmdfvyBUVQ8HfPh1ld9W1lnPS%2BpNm1rTpmbZzA6rFGoOu2WPm5nc70sL8%2Bc1gEqheOqX6973ngdPHx7ghxtVyo6O0%2BliOx0MnyBbkfxdcLn2V6YshDj3xLdPnv74nZH08GIo3KcfSSdJxkIYhg%2Br7bJvVrh9L89urnhLqNrcE9%2F%2BN339Y%2Fqrpy4sqkbvCVStD4SQ0uvIjrslhfLZN5%2BcvvQ4%2FxDbSUHA5o8CrwAAAABJRU5ErkJggg%3D%3D',
info : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAACOElEQVR42mWOS0hUcRTGv7kzjdM44fuJ1SJE27RqFbUwirBCcFEtyowEwWrhqkXrSoiQFqYoEQyJllEmFC0qSCzIwsRoMjMfjc6DGWduM/feuf/7v4/T4obOjL/NOd/5znc4oCx+r8VEKbMpZaZPfflJucAu4XhKkdN2P/11/ljT5fFXH21pEQWWozmBYGRDTCQ2Ry3neoG2xuND2YefvZvbCoRC60TEiSSd7Gbkk5a0iIiiCon8f6ZnYJyInM1nLtVUVwpul6TCW4CZXxH/6KSbra8shRv275U4JBWWCwVOSFQ4ODTqUqQUd+wxNOgGMhZKdu18MLYQnFrYd7jhfOsRboCbSMrgPlSUFwMQuE4qBzPATCQk1NcU3715Eigtq6vlADPBLWgWZAZV47ENUVBNKEw3bM+ADMSSKmCCSAO4Dm7AtMA0iBIDBCEtZdLyX2bAAJgODjicAqA7HEQAJxgAB2Rmvn0/XeLzuAS3G0AsKhZXlTgEKMDMbAjwzv6Ifw+zAo+Hc6QSmfVw6Mmj5/Fvw0LbiYOri3/cTh4JJ00TgXktIfo67nefbW95PRllHElRWVoJ9t4ZbD16AICDiAA8fPwGgFFUXVReUVVb6dkheIsQXM6sBtfm5gITIxPNhxr8/Te2Ajad1/sionqhs62wtBrAxNMXY/6Rxrqyxvrd9jYA0DZOXe3vGfv8QaWmjr6LXbfyXBe28bKv6/S1gaji8lVUIZXKc3Neyqb9ym3T7R2+1503/wcT3Lkk5CgrKAAAAABJRU5ErkJggg==',
loader2 : 'data:image/gif;base64,R0lGODlh0AANAKUAAP///wAAAL6+vqamppycnLi4uLKyssjIyNjY2MTExNTU1Nzc3ODg4OTk5LCwsLy8vOjo6Ozs7MrKyvLy8vT09M7Ozvb29sbGxtDQ0O7u7tbW1sLCwqqqqvj4+KCgoJaWlv///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCgAfACwAAAAA0AANAAAG/sCPYEAoGo/IpHLJbDqf0Kh0Sq1anwOBsGBwGB5gQWHA8Zq92Y16PSyf0Y+EfL5xd++ONF1Oxt/1e3Z+gHSCZ4Rzhn9xe3Vvi40bfX4GiHyKlYyBj3BiYQVDAl8SFRgKCp6pAhugGq4IsAgbX2xqrbGxCbS1t7iyu2y9uLq1tg++sMTFwrnAtci/xazHyMq81L7WwdjDzqu9GKQIRAYJCgsMDRALBQek7xIXD+oQ9fXs7vDx8/b2DJr6+PVbBxCewH7/8gWkh7Dgu4P+HO5jGFHfQ4r3JMrDSFChQY4JLSaAmA5DEQHn+skxxfKUgAUTYsrMsPKUTVQwZ8aseROn/s6d4nq+/DmB582hP43aRKpTqcucTYMehTrTqc+kUpdSlWmVadUiBxZAkCnrlVlSFtKqtbDAnFlXaNembfsWbgW5c92+jSuXbl2+a/3uvYtX8FnCffUexstW8SvAag0/RhzYsV3GbcEyiEDBAs2erzbI7EC6w+e6qEaXPl1XdOnVG0C7cv3adGzUtF+zfpsbtuzUtW3/7k16t1niwnGrLn679XLPsTVztpA1NAPG1Wdfx5tdQ4Ktabt/x559PPfy2+WKB08dPXnc7Ne/b51+rTjpFCSfgkyagWUF/HXgn2wBDghagf8hSKAEwQmYIIPBGdiTggdCWJuEN1E4oYWvmBkoHQQoyYZcUS0dlUGDCY7YnFAnBpdii7qtaCKKJS4Fo28i3shcjS7pmBxoKvKIQZCnEDAALBDEI2J8Byw5n1BMOnmelOo1CWSUVz45lZZacfmUl1dVaVMWBTSAipAHYCbjfmkWtiaAbSaGZmdyVkhnZXO6mWedG94Z2Zvx+JnXnnjaqadLWgxxxaKMNuroo5BGGkUWHwQBACH5BAkKAAEALAAAAADQAA0AAAb+wIBgQCgaj8ikcslsOp/QqHRKrVqfA4Gw4Pg4HmDBZjzgOAyOc3pQGLvJHHTaQGcn7olxgmNG0+sPdxt5g3x+ag4DgYKEZYdydnhujoiAeJeGan+Rg3h8c4h2g3p7fWd0iQWXjXF+qIqSnmaaawZhBUMCHhwSFRgKwBobBgUPAmJjxRoIzM3DY8h5ts3UCQZuYtIP1M3Wb8jF3MzPyG7h4t4b0cPb4s9veefc3uXJ7fPX0KPy1O/6G/y65VO3rwACDL0QENlVYQGDBhAiLihwoJeEixcfQIzIccGDAxUwJsi4kSMEBoEsYryg0WRElCMtxmzpEuXFkBgl0DTpseL+zZklOcLMeZFlUIkfMeLUefRkSpESjLp0qlTCSKk1kxLF+hBBEQ8bFnCcQPbOr1/MMAhYQLZtWQnA4jJLwHZCBrd34gJjttau2wx59SJQ0PcuXriCEfT1SxYwYrmE6zKeMFKvgrmSDQNOYJmvZMaBIS82bDd0XA2L/1ZOnNouhCIOxLqdgGADAg3LliFIUMGCb99kd+fGPbf3798LEtzGrRuD8d8TLCQnTlzBxePIEwzXzRt7cO3Di3uXDj63+ONkp1NP+/y4euKDe3mf8F43guvoaZeH73x+kQsMRECBBY1xpoEC1RFGVgcMRgcYgswxoyCDDZaG4HJpbZABhR3+FHjaMsBoyKGHy+mmgIgUOrgBhLcNNmGKFmSw4oHM4YZihQ8G09wGC+I4I3wH8TiihSW6eGOHHTwYXog9EthBBEUcEOCAvl2ko4QCMIBdlYhFqNgCW1pgJYQg0tXWcWPWKIyW+a3G4m5gtvkYfGuFmSZ8ZpIFnJhzlskmdHyWCGFYZ/52J3Mb/ImmBNt5Nl8CUU7pGwPl7aXASL9xSGmNl+32XIMdUPohe5l2SJ5eNOJngab7XWbdpx34ltyHwFzEIYWbMheXrbeGylliqrKKanG9+mpZrRKUyqCogl0qwa2yQkqAlALG+GswEh6pYmdBTjBijD8eaKS3KVpomQbZEhzZYAIY6JohuT7+AtmJ8JoqI7cnblhujsEEI4C+8SaGgZApPrlis/QOKaO8liZccAQbYEDAAMxAQBYGB4x6KQOFcqlxlnY+tteXhV6c8ZVwzofxqF+ONya2cJbcwcqnDQZyfjSjWueWOY+8M6Av+xwndBP0HNfPiyqQRQEQTUApw8GkG2ZyUA82EpV7Un1uulgTSB+7x0pwQNfeMnAwzBd1PenZll6d325QI6s2eXFbN/bbYMt1UAVzPx323diZjSqCXOOtlhZDXKH44ow37vjjkEeRRQBBAAAh+QQJCgAfACwAAAAA0AANAAAG/sCPYEAoGo/IpHLJbDqf0Kh0Sq1anwOBsODwEA0PwWY8HnAcDgMaPSiQ32a0YZ5uJxLje4JznvsNAw8beGR7a2p+gXiEg3yIh3aMZRxqaYkPeneTiH+Bg5l7lGlygAWEmWeWnZiaZHyqlnafmmaPaBweDgIFQ0N8BRgKwhoKGhsGAmJvBQ8Izs4KCMfKi8fNz8/HG2LVzNjPCQZjymNg387ab3jm2NHh1IMb3u0I4YVk89/pb/LX3/bcyLD7J27bMn/YAMZbZ0BCBQREBjgoYGBBAwgYMS4ocECCx48SHlzMqPFBRwkJUHoUSRIjgwcVQHq8wLLlS4cqU4YcSfJl/kyZNHlmXPDAY4WUKYO2hPDypMyaPYvi/KjUptScEqqSJIpTZ1aoGZuC1CmSQQMERTgUKKAB44S3b+8Eg4YAg4AFcOFmuCMsGgJ3CzLkjZugb0K8g/cWFvb3793BcSUYBhc4cUrDjBPgFZz3MmPKEzjD9ezXmea3oifwZTzsburQpIs5Rpx3r+TPphFngFBkTgLIE6RpQDAcXAULyPPW08CcboLjyJNPWJCAeXHT0C1M0D49AfHhfp9Hl768ufHoeal/f6bAI3ruy4k7z668uvzz49+Wp9ue/lsL1JnXmHjSvRWggPX411sBEFDAHWqFNeeXAgK81cF2qG1QzHXC/lTYwYV6aWheNBhskMGHtYkIXjEUToBiiBs6Q0yLKGpnwV7RrFjXBhZieKOKI5r4YoYxNlaiiyASeR00QiYZmop/kcgjiiBmAOWEQmp3YVoSROBgchZ4NCJg43EnJoKalfnWmeARc9p7YUpg3ZwbMIDeaJKZx1yaBaqW55J83skme49Jh5xHMYa3wHvbnblhMW8KKuecuakZp3UyKpCAnQUW4QADXkoXIGvuHPfhiwdOqEFKFlD5IQMRFleMezW+uhgxxYl36oUWwMpcNJmZ2gGYsD7al0e7nurrjMdKkKytk2HX6qkG3ppQBclW29dnulLb62LA/uXQqdIVUWeo/kTKBo0CTWL4ZLTCtGujlRgQE+WOSML1YzDDSIlkjX7aa+SUIKLoWWN/yZuuqnadmKR2GrIILLsOpzhZMSVWzCu9rDFZ8X8chxuvxqgRMIAzEPxnAQYHJDrbdney7HI9i5a5csufQYqXoTcXSWafErQ8ogZ33YmczOp2uDN3R+MMXqU8I9qXvYGCGfTFmtZs45o4l0az0T1vC7WgWRRw0QQOLhBxvwlaysDaUa4qwZfuUsevus/R/R+sdwPrEN28vh2MrHJ/KWoC/MYdtN7I8b2tMIvn113fpTIO4AaJR+mR4dE5LnbektvNYq4V6G1gMkIQccXqrLfu+uuwxw5FBBYfBAEAIfkECQoAHwAsAAAAANAADQAABv7Aj2BAKBqPyKRyyWw6n9CodEqtWp8DgbDg8AwGnoJgQyYPOQ6DY70eFMrwAdpATzvcCXJin+Bw6IB0eHB5aHaAdw8beWV9c2xqAw+McYaBBnh8ZXJpl5l8eQNrap2YiouaopeRk5SLc3V1kqh7r49rHGAPYllyagUKwRoKGhobBmOEBQ8IzQgKzcd6i2TLztfHY3mM1tfRBhvJet3e0o3VzM7QCObb6NfrCeDUZQbp3vJw9ffX8tqN5PqBE0fGnrdv//I8SFMBARFRDwQUYAChosUFBQ5I2MjxwoMGFi8W2JhAQskEHkGGhLDgAceXKVeyfFDBpE0JMVe2fEkyZ/5IBi4rnNzo86JLnig/ygQqoSbHpCpD7rRZEqfSlUBrViV6VeoDjSQlVGPQAEERDgUSMIMwoW3bDCXXrcMgYIFbt3AlBGu2boPdu23jrnOW4O/dvHuhQfMLOLDewc/qNkasGJqGuhkaC4aHGTBlfHYz390sDIFkz4KJPbtsGO9mbKEhFBkoofEEdgg04KtgwQLgBQmK8W2GgXdv322BC+9nfALyCcp1y216HLmF6N6K9/4dXPfwBM2fA889XAP158kTkL+m/bhb5cOhgd/etjf8+Oe5L29mvvmE2eFEgN4EcKk2GAYbTNCBc66pNhyCCt5lQYHeVSZABgseloCBxP4Ek2CGDXo3zDAXLuibbxQ+I1eJDPaWwQbELDfMhxK+6KBcNB4G40EQgvjWhtKtiGGEb8EonToJ+rbgbAVoQIF7vpVUYTwMVOeWlN4RtoCVjgnXITtbbnfcRsWUWcwGVaJnAZkiFpNAmoBhCRqXa0qwn3xh1ucbm89oKSaDZMaoml9cTsCmjG9auaedIuJJ338EcPBABE8i1wEDG1bWTE0ddJrhdcHJpVtJnSroKabLdbhRqRacCqSDCIDnqYnQhariM5x28Gd0wewl66wLxjpMr8GsCuylmcKTa6npwfpMU7O+l+leuUHrKYPAJdZnrvSdpQCllqb4II3W2TgYNP49tkggjJoikG596/Y6jIobYMjqhOzGp0C9GZZaoGJ80TXkc+byNSy/brloZGIzDqkjBqK6S6OJHRS8TsMgophsZQIvCCIBAzTElnOtYnCAlyrWVajJKDdTWHXHmUztoHmSPAHLFeam8oA4G6zAznrefMCN0ABNcgcyH+lyzTEPLYxiO4tpgcxyEQP0c1R7Y/R2VFMba5j0WZBFASBNUOkC7H6JAE6K1gqxisM09SSD6UHsYH9z6wmc3QBLcMDctO5d4agSVPpcrG8/bXLe2+1N7GJ/u+ec471GE7meyL6togI4GX4c2nwr5rfh0mIwojMbXH6XAFoMccXrsMcu+wnstNceRRYfBAEAIfkECQoAHwAsAAAAANAADQAABv7Aj2BAKBqPyKRyyWw6n9CodEqtWp8DgbDg8Aw4YE9hQy5vvhyHej0Ym8lfg8Exnw8em4R+H5fL6W1lCXkJA2p+h4GCeYZrdQZ3b3pgfn+QD3pmhRyVf217g4yOlXeghJSIcopke2mHdQ6lmWVfa2AcBQVDQ5wPDwYPChrCGsUbwIsJBQ8ICArNzcd4oXnL0NcJyKGDwNfQ0ovS3tEGrKzKzM7Y2mbW6tDZ1ITu3tmS6OMI8cn063hv3cYdGzQITwEOB4gY8nUhwQMGECJKXFDggISLGCU8lDgRk4QEHy86bMAxIoMCGUNqJFnyJEiQHxNc2FhywYOMe2ZCbOkRJv7GByw5MriJEebIkhCG4tQzM2jHlzhpCvVYVCZQpEMtgswj4ZiGIrgaVhCwYILZsxP0vGuGgSzasyCbPYNX9m3aBPncvs2g9tlcBXrfqoWmoHDgs3zxFkZQDHBdwRKuzU3wGHHccYfhRi6sQW7mCXwlCHM2zLHd0OPaIoBQpJzICnYtINiQLwFsuwvwrtVXwYIFtBZy14b9G63wv2x7Fy8uPPVt4M3fKbg4objZ4LqfdeZtd0J0udMl+H57fK3ts+O9Z1c3/blx3c0aU5/Q2nbb7hk2LCYMGH+CxX4ZBpxZ+RXmV2kC4KcfZ8T0d5p+jI2GAAYbZPCbdQXuV4wGFf5eWFyB221HYQboEQjhXGxV+CCK4Km414IoFraBXR3wtR80I3ZAHwEGXDDWAuMV18FFzvw1HQPpaebNMBIgaZ1ZRAqj3ZEeXnjRhlhuwMCAExDJWJaVVWeBl9AUk4CT100wZGRyfanldctFWSaHZV143ZXDbKcPkE+mJUEx8TVDWZBneTnMl2ee1Roe4gnZQQcMKGbkRY9e92ikcul53qOVQprdoRMSx6lZuUmIYwWVWnecX4IqV52anhoYIKWjXvqfrIXRyqmtkjkjAao6ovfdXBgAS96tgfKWKqyYstpqeosWAIGQJiJnYIdpgrYBBm2maOGrJmJw44QuUgvSofDEVqijb5XGxaqMZlVa7V+lYXuWjraRli6JYj6aIWlFzrgiwNdmsG6w+YkbKLzZaqswruoqOoAEBigA57py7kdWktXJSdjGvoU85gGZFtkknHeyOeWR3WFAsr5MbinmWS6DZ3Jd7PpWM4sYJCpmcS5LqU7PfP5sQc1FIljnW2TiSBnKHYuGYszpWZAFShBMQEGQpYLqq6uqJiCu0O1tfSwGISZHAdS5PWzyARSse1bbADMJd59tM/j12knmDeB0d6cJKYzgSQB3v6SKXaSUhm8tsgWRjr1443iLrafaaAmgxRBXdO7556CHLvroUWTxQRAAIfkECQoAHwAsAAAAANAADQAABv7Aj2BAKBqPyKRyyWw6n9CodEqtWp8DgbDA8Qw4YEe3sCmbN1+Hes0ZkM2JOEdtcNTVnEd8X/4a/nZqbhsJhIUJfnd1BoOEjohzi3+MD3CHkYB3A5WWkJN3DptxZYeJn2J6h6UcmZONe3GJgXZuhWdDc2AcBQVDBX4PqQYPChrGxggbw4+EBcQI0NHKnIYJw9HY06SO19jQytukzt7fy4ZlwwrkyrZwzure1pyjG+PrBpbND+QI8szT/MCdidPNm7ZKBQxIIOLB2YMLex4wgECx4oIHByRo3ChBYkWLeiQk4HjB40cIDB5sHLnxQYOTKEOKZFly4smUGvdoLPny5P5Flhw79vyIUyRJk0RVzmSZwCXMn0E1Or2pdObOqUkzEpr5oMguiBjCClgwoazZCXHIYRh71uxIeNjYtkWbgJ/csxnSwlPA9+7ZtAj4FlPgt2zeBOqMqSNMdu5IaHCTNcb7GFvfyX8lFIOsgfFctBICd4ZXeELe0NEwVKjQlQA+jXw1nrUwwUIyfrLnLkBMTkKF2bV34/7d1oLwwNhUl6Vt9ni02BKATxC+F5pys7SN89aAzfdn53yhedddF25n2RaY1+63GLmC3G2PK4ZexECCCpsTfM4bPrBgAXNZwN9i8GCwwXKULTYaXwfOlcEGgh3T134QbgYZYbW19SA8yP4gYCCCE3TQAX8IcFcigyAaVmE03GFY3IbPCbZBBg5C+Jx1DZbVgYqRyZhBfRfg9x4DGYZY1lsXfoPZchqxmB+Rc2nU2YnGJLBAetmVJeUxx1iZogVbctkPlLVl1yQ0HW7QGG07goZch2OlB2JlJY4mAZHMmamZiXV6ORuYqJlYJVnq1SZlYiayVZ9SF0QXoogiMsCbf9AtB2kHkloYjW+QGmlBplOmVkGnzdWl1m+FTsdbZN6x2eZu4Qn2ngQiPhopYrJCdymkkkJGKXy13eqrf8AuJ19yqD5aW69wVTpBfQVIRWObPBKIY4qmrXihAjOmCqOT3GbA3I4CbhDWXvkMimtBreVi4N611Nb2oLuD+WhWm3mdi2a66dWaLV8tjjZjjZvFym2w2M1bomj8zjavrP4N/OwAEghwQJzrFinBAf5deCdwgFoWGwNYyjkBBgcUPBjGy6W3cWJ84VfaBC93tuCdJjOpGYGdYdyBnCGLqoCVWJ6FcnsYXFYm0C/Xy5efbW2cnJImMyf1vtDIZUEW0ZZBAdDBTfprdKnupq8C+CVwAAXkucunhxV8XajZ9Aqm0dezSRrWibGt3faUdkuA91l0Uzor22ymp3fdfX8dr9kAFxh3yWWZTel5fuedgLveqMb2BAJoMcQVpJdu+umop656FFl8EAQAIfkECQoAHwAsAAAAANAADQAABv7Aj2BAKBqPyKRyyWw6n9CodEqtWp8DgbBQIHg4YIfY4SlszujNgDN2sMmPc2I+fxvahsGDvqGD73dieXFzfQlqdg6BeoeNfWEGkYuEjXUcgGODcnQJbJFtDnpyhp2KkoKihYVrpp+DnI2sgZKiaRtDYBxcQ52XD3t0Bg8axBoIxBvCfcsJBQ8I0NEICcqwwtLR1HvLZwLP2AjJcZvi4OHChWje5tRphc7sBoaj8ODJ8+/f2MmOy/XY2siVs+cgTgEDEoiE+nWBjoQEDxhAmEhxwYMDEjJmTHAhIsWKezRu7CjxIwQGDx6KfOjRJMqHCURCbGDyJLCYKjvSNGlxZf5MkjVR4twoQWdNiw41zjyaUuacBzs/MigAU6RRlwUOxKRTZICBhhjCIsAgYMGEs2gnzMGmgKzZtBMyrGVbFu7ZmODc2lUrAZqCv2Pr2p3zV4GGv3rhyk1ATAECxILhEpZ2ODJauX39OlZgGS3ex4f/do4bM3S0xJIzY6hQQULXDRkBh6tw1sIE2xammZNA2+6CBLt74z77O3ha3L8dS1PA2/bx4pof866dFrryv9Nva5/w21g07L3hFpdtOLt44NBCa2iO1nnyx6CZh6/OODTiIgYSVDDcWMBeuW0VZhhnx50FoGyPEWhXBhsE+Fdo/i2YAH/2KajYhN8NuEEGC/42SEyGG57VQQcGNojBMfwpEKJiJh5zDDIcUhdXgwkKuGJaDG7m12EbiIhjiw9qgAF+F+zH3wYM+IgWbwmmpwGSBfKFojHlJWkXk4cV8yQDw9XGm5bFJMBle2d9WcyLCZhlAW5rYukdMWnaRqKXfX2IogRvOddmneptSR1uGUVj5wZv3bamBV86CWeh2wVqJzL4NdUWbyNWOiIDwAmImAQWWHobdNhQOgGJ7mEYagVz0mecb6ZKM92IqgoIzauWdoCpprOiWqutGGqaXaeV3hodAubddiljbNGqnQWgAiYfflRh0JYAMf5YGIjVXkZjk3/dqK1yhyUYIqklXmdjBumwOsfgieaqWK26Hmrq7qjWpvegiqOmOuNm6uHb4bUDzlsbrHgNKO675Tpb2IYEDCCBAAdU4Ba5dJr7mGDDIZqZrwscWhuiByRoH54fAypBaEauZ+Vxsdl3GMlkaiwyZAt0sOaygY5lJGcdw6WxY9IWJqbHJlf4V5o+gwzNiUtjnJajygXWcRZU6XfAARkThx7AxaL1G7spS3AABawyba/YZJ8XtLO8pb3d1/ZJV4HbXifAroBtl60pBmiPejN3dsubt9rhgob231oHbR/fbh+KqdkJPkSBAFoMccXlmGeu+eacdx5FFh8EAQAh+QQJCgAfACwAAAAA0AANAAAG/sCPYEAoGo/IpHLJbDqf0Kh0Sq1anwOBsFBwGDwDjmM8/hY26PQmPBaTB4+EfJ4QG8iGvCe+mfc5HHdlenxyfXWBXmQOcAl9h2uBeYuNh3Nhgl55jXR/i5SFfnaCeQaVdIilmqZxhoZ2q5sPagIJD2BcQwkGAl2tfgUPGhoIw8MbvI+OCcEIzs8Iu7V0D83QzruPaI7W19KQaN3Qu45p3A/X2MmuyOjp5NuP4s/whn3z6uX6+AjI02r86hQwIIEILwm85khYKOFBAwgQIy54cIBhgoUXHEaU+IChhIsSMj7cCIFBR48MNZKcCHIhSJUbTYK8SBNmRAYFLHq0CdGk/k6MPCGwRBkyKE6Lcj4aLdCSoUiSQn8lKCIAA0UMWBFgcLZhwYSvYCfIeabAmVWvYSdkGEvWmQC0admaRaDgbdqvFxXo1coVbti8GvRu7ef361oJZQW7LYxXwrPAi+8eppu4712xCRQE1qvXbtq1mZ9hCOw5LGgMFSpIKHLAFuKyzzZUsDCBtu1o6RAsvGthQYLcEmbXDusbeIWvtmsXb6v1OFjaE4rvhY0a+nPfsPfqFm49+m9n03ffxQ6e2PbLxTcH1iC+O3nNsIPzjsZZAQaqBDUPgy/gcoYN9RHTGXKmZZYdfBvwthZn6w0oGYABkqYghHoZo0GCkoUGnlkJ/nbQgWkArmceBhtkkKFmxQioAIbWWfBfffrVlZaHC5bHoYmfGRhjEQ48UEGF5iXAwIdhWbAQbPtpICSBYB0p4H5LpmUkYhZqlsAC3VkwpYXGbMAAk40Nk2JgV9pm5kLmWeglkcNt6QxkhEGnZW1oAnkMA8nZtlCKb174pZZzTjmmeZ51h2afGhQBx5GDoebho19hV59ZFUAK3XLXODoccgx8R5YC4oHlYafGzdgBqeloKuqpOpK10KOwdsoZeKBKAOujssJYqwW3skpZeRchF6undDkT3Ie2jfrbdM7wmBMG1JX4IGzF6iXtZxRSFti1z70IWbQmtvjipNaGW+CB9Zux+JWHYkE7q1bcTsBuAqNVSFm8hoUIZLlhzbsVfAiGy65amX2rlwTqyvshvZRRR8AAEghwQAXuSoBlkRZgcMCvsL2V51caw9iXnLx2EPJmHWMZKJ2I/YgiYRhnfACDiVnMpJYnKwbqxXkuhNqsOxf5lc+TxtmmlkcW3dVzgGo8mFZWXlzkkaJZmUVOCVAM6gHJcZotdc4x3enPMSZwAAXjMVwMZwijnZZv7jYswdmb1sbABnGzLYHb655KYXh7Y3x3fdAqufectVkQjbv1zc03WHDb2NzjWo6NMqVoAxqp2uEJoMUQV4Qu+uikl2766VFk8UEQACH5BAkKAB8ALAAAAADQAA0AAAb+wI9gQCgaj8ikcslsOp/QqHRKrVqfA4GwUHAYCp6BYzw2ODyFjXo95DjcZMfgkajbExy418Cfb+x/eHp8fQ+Bh3lecXKGCYGOeXyLfnd1A4OSc3d/G4mLBpSACWJme2ahd5FmhKB0joCJrIVsdQUDX0MJBgISDw6VagUPGggaxsYbBpyvG8IIz9AIupV0ztHP02uv1te6h2oJ3NHe2sEP19jKdcvi0OTM4efo3uucAvLdypyc7en14P0Q+DIggcguBQoMXKgjoaEERw0gSJy4oMCBhgkcXngQcaLEBQ8uPsS4gaNHiQweOFyZwOTJlCwfbuzoESTGkQlmnoRQ8Sb+Rp0nQV7M+NOlR5grGwI9qvIhQwlLJyJNGvWjqwRFBChIeQEDBgRfEQpYMKGs2QyOnimAhmGs2bMZoa3FRvZt2bTPwgqs+xbtBoRg1bq1OyGjBoRfnz3gC3cDAsTPNAy2i/cwtMlwJQCeqwCzWUeHEQL2PAGtBLaWEzAui/ZChQoSihxY0BbwuAoTLOTObUEaOmm431qwsCDB7wS4db8tfjz5bt3M1boLvrts8c3T7Q6PbluBBOfLjT9eq0DDd8LExZfPTrh46MOHzys3y7z8XATnhU+4jriIBA0bYHDMehgkQFhpCYhGjGgbVAfXZuUd1qB+aCEE31oFZkBYhQr+EpMhhQlaaAxCE9pVoXTFKNBgByb+FaFcJfaVYDELPtbgfLpl4KKIYCWgYVkdsKijgjAeOGSECvhHB4/PCMCAdhY8FBl80pA1313/PTZildpN8NAxVKomnG5fgnmYW1dGmeWAklmp23BeimfmZG+qGdl9YvK2W0YiGoOAk8/t+R8xNFY5HJzDNQSmO1ZSdhqNGhQhTEMKJOZdBUEGWVZ6ouGJKYvzRdcNeLuJOh5wb2nKgHjRKFCBgWapyqpcr6bK4qq2PQZcpplOgCtnCDXEq6ydAobcsEH+qhYxyOXG636sevpcpsrOVcQcEmAwl4Q/9vWXdAx2e1aA5Nmnorj+OYaoq1o3mqVcAtpuxu2VCJYbWmcZKKfpBDsCu0G3mh65nn3/brjjlufGymJh2r7XY777lkbueBj6qO/C5OZKwAAJCHBABfEqkOemw2FwAHbFoOmgyZ0ySrKmJt/LKKK7mVyBhYLxdegELNunK6DKHRozZOWp/CbPH4v285Obbtpzdxs0eqgFT98HKG9wmszWeiM3rXVe62VRwEMgB2sgjpySp1Z+JEMb75YPUdAevCkaW4HcdhWnLcXeHUABjm6XazbeE2iqt9LB3u2ubqvG23Dff7d9eKfm+T13WIQCRzick98ngd87W0c3dg9oMcQVqKeu+uqst+56FFl8EAQAIfkEAQoAHwAsAAAAANAADQAABv7Aj2BAKBqPyKRyyWw6n9CodEqtWp8DgbBQcBge4AHHQTaQHZ7CZs3eiM9wzyNBr7vHhrx+ME9s6H8ceHp5fH6AfoJwZ3x/iAmKZ3t9j4p6XgaGh2sJb5KFlI6Qg2ZkmnWJpJOidYKEe2oCdGAbBUMJBgISFRi4dn8CthrDwwgIGwZ/yn+2xs7GyHWUzc/GuJyOtQLV1gZ+2NrcCNfLCcHb3L7KhwUP4tHr5u3i6vHz3PDl99XknPLuzzBIsEUkl4IFDBAUOCChoUMJDxpAmEhxwYMDCRxKk0hxokUJGUE2vBCx40QGFx86LGnyo8gEAiFy7Igyo02BJGdSrKlxpf5Oj3M04mTZ0SLDnjJNQqhJ52FOpShVNvSjs0EDDbmKCFDAYKIfBBgQKFCAc8GEs2gnZFTwjKwAs2knZMgo1hkGBW/jnqUblq3YvHr5GiOLF27ctWGdFdY7VwJZYxr+Gk6bUYPfwYAPS6hbd3Fgx2P9ek7bGKxlzJPPNpZQ5MACCBOO0aswwUJt27E3cFPQEK0F3ONm642dQLht3BMWFL8sdlftuMo5D+599vdZ5WMHW6OtFztbBZHHca9+PQH4ts71jgPPvvn4tMpPK07/vDzZIhIYRICwgXD4sQIMN9dj2QEooHljFWMgear1155YCmyQAWMOFgOhhAzK1R8Gw/6IFiBjCEJ2oYD9hTdYhBPGlcGGdRGDYX0aklWMixN20AFaK56nWIR6dTCgZfjp15CFmDHgG25DWvafAEbq1RB48o3TJIyVKelMAnBZN0EHSXY4zAZT1tcllBokwMBxyHVJjAZgPqflkMyNs4B1aKqJwDBmuolWQ5BFhuecaP6WpDN4holWlRrgtwBej3GWQAU2ktcBAyE+kxGMNlIqnKSTFgfhdNxFWl5bZFEXaaaenljqWaISp9h0Eth4aqeqjtWQrDeiGtplj+Iqq6Zi/bcLrqxqepmtEtTmq7HGtIZXBX5FNtaLpDn46bQkhnYathnmyBy3MHob7IgUJlggiuVb3thgdp2h22qO0v5HLatqSdDhiRuwqm6MCeK7pYoOsjttjfuK2+wAKD3AS2h3AfbbwwJxpkHDZiF31pPfvvVbqxjbtdhxezr2n2S3ISfQtqhV9+YBBErLpJ4Xg8YZXkbihqTMnb388M3sFrlzyGDpSLNvaGFwQNCRZbHQo3cRRt1tqM7cXLK3pctAiVFmREF3MIUG69bQde01bweAjZxyTbOrdbpoof3dd7tsrWWnGNxlWallV11e2tI2BDZ8MJnYXN50WkDpXTsKRAFyvx3+HQIPaDHEFZRXbvnlmGeueRRZfBAEADs=',
loader3 : 'data:image/gif;base64,R0lGODlhEAALAPQAAP///wAAANra2tDQ0Orq6gYGBgAAAC4uLoKCgmBgYLq6uiIiIkpKSoqKimRkZL6+viYmJgQEBE5OTubm5tjY2PT09Dg4ONzc3PLy8ra2tqCgoMrKyu7u7gAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCwAAACwAAAAAEAALAAAFLSAgjmRpnqSgCuLKAq5AEIM4zDVw03ve27ifDgfkEYe04kDIDC5zrtYKRa2WQgAh+QQJCwAAACwAAAAAEAALAAAFJGBhGAVgnqhpHIeRvsDawqns0qeN5+y967tYLyicBYE7EYkYAgAh+QQJCwAAACwAAAAAEAALAAAFNiAgjothLOOIJAkiGgxjpGKiKMkbz7SN6zIawJcDwIK9W/HISxGBzdHTuBNOmcJVCyoUlk7CEAAh+QQJCwAAACwAAAAAEAALAAAFNSAgjqQIRRFUAo3jNGIkSdHqPI8Tz3V55zuaDacDyIQ+YrBH+hWPzJFzOQQaeavWi7oqnVIhACH5BAkLAAAALAAAAAAQAAsAAAUyICCOZGme1rJY5kRRk7hI0mJSVUXJtF3iOl7tltsBZsNfUegjAY3I5sgFY55KqdX1GgIAIfkECQsAAAAsAAAAABAACwAABTcgII5kaZ4kcV2EqLJipmnZhWGXaOOitm2aXQ4g7P2Ct2ER4AMul00kj5g0Al8tADY2y6C+4FIIACH5BAkLAAAALAAAAAAQAAsAAAUvICCOZGme5ERRk6iy7qpyHCVStA3gNa/7txxwlwv2isSacYUc+l4tADQGQ1mvpBAAIfkECQsAAAAsAAAAABAACwAABS8gII5kaZ7kRFGTqLLuqnIcJVK0DeA1r/u3HHCXC/aKxJpxhRz6Xi0ANAZDWa+kEAA7AAAAAAAAAAAA',
locateMe : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAYAAAAmlE46AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kKGwgzGjNX4ooAAAJcSURBVCjPhZE%2FaBNxFMe%2Fd7mUpnfpL71LQkg9IVr%2FZCikf9SC7WAJSRRrrUPpoMEiSRdxcwnoJOggRboIWhIc2qLE4iTYDqU4WB38Q6HqItKKtZfalMsld81d8nOqBGrqg7d9P%2B%2FzHo%2FBf6o6N3oF5tYY7LwflvUMHH%2BHjWQ0Zl%2Fo5cVJprx6DfJ5oNELUAY09%2BknTPOYbR9TO7P95jGkEMA2ALQKMCwYu%2BCEtm5ydXXU7MfOOlDeBsABpgFwRcDGA%2FrGhfpgefMkKmVAWwMcVcBWAmwaUMoBDJuvD1a046AA8l8AQwXsImDqgFEA5Ni9%2BqBVbAUFUK0AhTWgugY0eEB9fXfZSGZ%2BF2xvaWmZNgwjwPN8ibMxm9TSvQwFsNvC4Qp1d8fZc0%2BnAYAjhBwslUrvOzo6fg8MDFyfmZlx9ncf8rHW6xQoAMYO6u5SQIJRNpL5%2BHejzs7ORx6Pp5JOp0Wfz%2FdWkqSdG5fDOZpxUvrERxfHzxaDbQce7DklEok8F0VRBwBCiDUyMjJenRu9amVPa2OXThR6e3tHA4GA6na7T9VybDAYnFVVtdHv9%2Ff19PSsLiwsnPmxWfzONh9tXf5l%2F6AoSiIUCr0SBOHhHmssFlsnhGwkk8muaDSaFwRhixDymeO4yuDg4Gw4HL7t9Xq1WoYDgOHh4T5N05anpqbmnU5ncmJionllZWWI5%2FlFVVVvZbPZdy6Xq6goyt63pdPptng8%2Fk2SJMPhcOREUXwhiuJiU1NTgRBCE4nEzdo8848BQ0tLS6l8Pn9E13Uiy%2FJXWZbvp1KpydrcHyPz5blPQjIYAAAAAElFTkSuQmCC',
map : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9sBEQ0rMteXYLwAAAKiSURBVDjLlZPLaxRBEIe%2F3Z11N%2FvMxpFoXgfRQDBCRGIEEQQfIOJBEERRFG8eQvCg%2FgMiHjzpQcQHBvTiTRAVMSpqVAxEgxoMxmiMT5KY7EzPTM9Mz4yHmCwqItalmh%2FFr7%2FqroqtWrsqam5dipZI8L8xOPAarbl1KedOnftnsaOcP7TOg51oszfHgsuIW88o9diMH2kim88xNPSIQXGWrxMG2Vyeb5MGCS2JYUn2bYlIaSnis25GeZJyq4YhBPaNQYT4gmkKYm4%2Fjm1gCRPPETiOQElrjkKbPRSK84kin0yXDsf6SW3Xqa2rI%2BO%2FxVHN5Et50sk4WnIewpYkEhKgQlCe%2Fo6wppnSFUZJ4VwZhiAksh%2FguwJpOfiujSOm8Vz7T4Jidf3PXINxQFI6PYlat4iU7tLkTpDMN5LWYiRTSUzLA4wZgkRsxkNKF0vYGOVJzGzAVDPI7pGZIu86115s5fbIFqQQhF7lDeZaqKqqRq%2FWKRQbyOcWkNzTRtWoRtqrpV7%2FhKs8GvQWrg5vpq3%2BPMl4%2FNcWzLKFISQ6U%2FR9%2BsrNV31EGxTe42G8QFFXWkLLog5MaXG4p5uTO3b%2B9gvVCzGUy2Nh895No0KPja17CaKQIAwIifg8PcbyxjUIz2HPxQ7a2F0xeDL2jr6PQxRyGSxbIJVHEIWMTrzBDxUq9PEDH8M1aWtci%2FBt7skLMwaOchgPfLKpLKV0AS%2BjkEqiAkVtsQkVBARRyJfyB2pyC%2Bkf66V3uJf1xv4KQZ4CTsomEWXJxFwc5dL96ARe6CGVz2K9hdWLN9E3%2BpCe1%2Ffp6XrO8aMn0YJIAdBQs5KGmsqidCzZ9svi7DrTTixexdORAe4cGpjTY%2B1r2qNlK1qYnYe%2Fxd3iJfwwYJO5d057%2BewFPwAsnUE8ZPBqbQAAAABJRU5ErkJggg%3D%3D',
mapToAutoTour : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAeCAYAAADTsBuJAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAExgAABMYBQzIXCgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAA1rSURBVGiB7Zp5lFT1lcc%2F9%2Fdevdp6q%2B4GekGWRhYbg6IYCBqJo4i44IwBVBSPxkCjTkbHMzOZjMlxOjNzJqPRURmNoMQcF1yYk8kxGeMSd0QQjIqgLAo0SyM00NB7d9X73fnjVRfVbTXKYpyj%2BZ7zTr3ffn%2F3vt%2FdfiWqypHi6X86bl9jU8fSLa17Zt76kHYc8URfY5ijGdxv4MjY%2Bd%2B77cLhA6rX3jFLRh0ror5OOCwB1NaKqZ0pXndZ%2FC4Tsu0y5ZKaqlMmXrxswdzS6489iV9tuJ%2FV4e7ZMjLkFV0YjRVOP3XQeVWtQ9c3AcMBsF2qfhdd%2BzZSfcKoRFnl0DsXXVd%2BQefeTy69%2Filt%2BaKJ%2FyrgUwKYf6UMFDc2JRJLXJaXKB81burfxo8bdUZRUf8hEooW8tL9l2aMhvWtqigiQqq1gbho%2BOwLZk5989Vn1tw127vkpke6%2Fvin3c5BiIgBilV1z5dFw%2BeBZBvhB68re6B44Il%2FWVI1IVE2uNqJxIpAFQVc1yVW2J8Vj3y%2Fs6Gxc4%2FjiFaVRctGTrzcTe7fjJ%2FsINXVTirVgarDmjVrGj%2FeuHbCzU%2Fohi9lYyJFwC9U9fIvY%2F3Pi8wJmH%2B%2BhBPVw6cOGnN%2B6bpN9exqWkc4EscLR%2FHCEaKxGOUSo6ioODzsWxdXFhXE2f7WIlRcjBvG%2Bl2IcRAMNtXGiOHHJ3btqr8W%2BGHvRUXkBGATMAn4WFU%2FFpGzgKSqLu3V9xtAGbAUGK6qq0VkDLAdmAisVtWtvcbkAWcA%2FUXkNFVdma4fDFSq6rJjx8KjQ8YIayJ89pARYws%2B2bUT1wHPFbyQIew5hMMeYc%2FDcR38rmY6m7bS1bSV0iGnUffWYppbWnGipYgxwSMORjsJh9xpfax7C7AI6A%2FMF5HFwDTgVBF5HEBEEiLyG%2BAi4BvA48D89PglwKMEgrleRB7sNb8HDALiQKUE%2BCUwFzhJRP5HRI4%2FevYdPTInIJof%2F35Z5ZD8uvUHiMXi5Id9wmGLFxG8iCEaDeGFHEyqDduykyQRHGMoHTSGtsY66rYsp2jgaYQdxZouRITyivIBd8%2BSATcu1l051r5bVVeKyHvAXao6C0BEqkWkHJgOLFLV36brVwL%2FlR5bCUxU1b3ptr8TkUmq%2BiqAqu5LC%2FXbqvobEZkMvKuq96T7LyUQxj98ATw9LAQnQERcxxnbkVRcx%2BC4EdQrxomX4eVXgAitu99nxxv34RPGiRQRySsmmh88JYPGUDrsTPbu%2BJCdDY2kJA8xDhUDygtD4dh3%2B1h7U%2Fp3M1CfVb8NqABOB17Pql8JdKXfP%2BxmfhqvAd85xD7PBN7IKq8BRh%2Bi%2F58MLsB%2FzuK0EwZWFTTs78JxPaxN0tawgdYt29HWHdhkB0mTTzi%2FH4mikWiqHU2C2qwwwk%2FiFg1j375GdjRsIy8%2FweAExguHZwP3HQFtjwPXAneky5cTqBaAk0VktKquTZevAO49xFyPEXzxb6fL5wJ%2FOAKajjlcgLx40ayqquHFO%2FfvpsDvwvg%2BGAP5CSjsjxgH14sQj%2BWRlxcnFovhxeMYMYAP1iccbaMg1IIbLSHW2kxrcxPEBlBc3H9Iba2YW29Ve5i0%2FQ74qYg8SaDL64BuVbYS%2BHcRaQcUWKrat7elqutEZEtaLSWBZuAnh0nPFwIXwBgpi4ZdqgY4gAfiYETAcRHHQ4yHOB4YD3EMOBab7MCKA4C1KQQlHjHEXZfieBRbLBgxFCaKonyAB2RyRap6ZdZ7CzA7q%2FyvWfTdIiIOEFbVtqz6JlWdJiL5qtqca2Oqup%2Fg1HSX7wHuEZGoqrYfBc%2BOKTJG2KpF1KLWR9WSsj5kYgTFVxBVFEXTsQGqCIqqDR7ro9bH%2BinUphCvgGR76%2BF%2B%2BT2gqj7Q1qt6W7qtJ%2FOvkjhRdgATWKDr%2BpivB%2FOfPkPyO6M8krOvsHDm8%2FrMkdL%2BeZARgIigvo%2Bqj1qLtTbr3Wftig3U7WolFHbxW9vgQDMdrsfoCUM4riwOHBRe929IPFTtkadb%2B4CqzsnZkI%2BQohDF%2Bbxz%2BRE84OJcbaJfvJ3ICEAxWHtQAKoWTXahRli%2FegtN4TwmTxnI2ne2UT1pKDaV4q3ldex4%2FQPejCaYPnUwaLcAAuGBpJ%2F%2Fv4h4tLSkuAYAZT6QB9yO8IERln%2FR62cJQNJfbloAfor6FavJHzmUT1otp5%2BaQNVSt76e46v74znKN8dXsm9wHiue3c6Blk4KYhKcnO7TAxxSAHNlGvDXCGOBZpTluPwYnwsRjuN%2B%2FXvmyBgMC2nkdJ5Sv8f4GpkFTKKR60nwBhm3msXUSDtwdUYVzZOLsdyAcAqBEV6JUjv1GV0L%2FApgyWS5HchTeH7m83rw6xeRJecwB7gMOBHYJMLzkQg%2Fu%2FDpwDY9NVnmCFyB8vSMP%2Bid3UOfOkceEGE48OMPzmBZ9VJeAg4QRPY%2FOOhHiknr8ECFtK3byJY9nbzzdh0nVfdDsGAt5YOK%2Bai%2Bma6OLprq99JU38ikwQ6NLcngBGWEmOaV9KGB5sntCIsRXsFwAcLVGHbj8zpwAcrI9CeSD4xndE5JlqOMZgkW5S407e4qj6DchZP2mmrkbpSHEV7AMgX4HrAPYRVz5a9yExigtlbMknP4HbAAOAvoB4xX5Sft7ax%2B%2FBwZEMicKmCSGkZkjxdhHDBJldLqtQhB%2BuUi4Dag4mA2VAyqPtZarO%2BTrN%2BJFvajqaGFeNhAZyep%2FU1UloRY8%2FYmGsMOO1uVPV1ChZuk44%2B7qDy7LEsNBb%2FkunGbK%2Bcg3IhhPL%2FQd7JaXqNG3iLw2397KMb0QJBRfIIbJI8UDwG%2FZ2E6Rpgn5wHzgHEs0PezRr3MXPkQYSE18joLcmdNT1jKzcD5QKMI%2F6YuD5NkMvDPwHBXWEgfNuQQEIRlKvwoK5IymS%2FYaoqOpFLqKamUz56tu2l8aSX23Q9ZtqmdlBi%2BU5KkPRLhxskJbhiWZEhnM%2B2dyYwnpNZH%2FRQ5bbBwDfBoL%2BYHWKCLgRWHuaG%2BocwGHu3F%2FACVzCeIC87ta7gIVwfTcM%2F05%2FWOGc9ow4wXdLEoN6e7XPTY%2BVJw2GQJP5v5nL6WEYCIk2Ec1rI5WsrY1m2MCHfSLg4PNyZ4s6SKiV4TgwcX8H4jlISV4k92U%2BT47G6z7G1KBS5q%2BrF%2BirTD2hsnEwRTfeG9w93QIXASmomAe%2BJWtWjfaYmF4ySEcgKAwpPZbVrM74EmQNwk1SrpjWomWu9GONfcFt6F7CtJ4wbLAEaEseOrWNTYj6JUG6dt%2F4Cbxkf5i6ooWCXev5CdLcp1xY2U%2Bh2sbIDH6sNU9ovhuF7mMY5BVHNde%2B5CqcjJlABlmbdUOk%2B0m9Ic%2FUbkqOsJpR6hX5%2FtQgLIqX7mrtIksAPA9M41HeBkoAAg5FCHsjmYjhO7u9TWigGG5prb6wqYbQCsqjFOCMRBxAFjcF2H704bjf32t%2Fj5xgj1a3cw6uM1nFHYwaRd6%2FnBsE4KHEtzEpZsNfxo%2BkBCoTBOyMNxw7ihCMZxEEGWvEphr%2FWXIszkGol8irJrpYLszT6gm4HtJBmbYx9n5trcp9aCS6iVT1%2B%2F1sgwYAyHcDcF%2Fjf9euOSKXIqwJLJMgjlX9L1717yrO4UzST7xvz6PCmvrRVT%2FQZXAZ%2FeYxYMgG9xcUI4xoAxGONijItjHCpKYlxx9QRe0AEs2234uAnW77P8ehPUvufwq7owkyocNm9pwRLCdSO4XiAA13goxhxoIT99RRjA5TYghMejXC%2BJTP0cGYjLfwM9o2fhVYSbuErimbp5ciXdd9OHQog7gUJ2sIhrJT9Tf50MAZ5AeZr79c2%2Bhiv8I7AVGIll1ZLJshbYhDIV6LBOYCNmvMiHwF4g7Ptsrl7KRpSHPou8bgE4BoIckNN9ChyM6yImRKJ5PzUnehRWlHDLxiLu2l%2FJc94QuqqGMejMk9gxZBRVoyqIxmI44SiuF8ONxHE8DwySTBGFrOj0Xm3B51yUEnzqqJFXqJHXMWwAXkX5ZQ8qU9wC9CfKNmrkOWrkfZQfovz8MwVwr7bgMAXDUFy2MU9epEaWYVkHbCBJ7qg6jRkv6AFCjJMgO9sJVKebXrZwyqXPamCvVFUs0whUVpjALX0ZeOVQ8wfHUhFVP7jNUkUNGEl7d6Is225ZtbqN8hEDueLiGNWDCzGGwMUUGDX4YM6ILKNrXA%2FXGKzi0jsie1A%2FAs5ijoxBOAVDCljB%2FbqRGhmGIZrVt46%2FkQm0800MJyJsQHkTpRg3yzg20E4JZ2EDfZzBfboekUnUMAbL2CCBxaqMq3qQ2TltxYxntAGYtXCchBJFDO9wqJv9nLb27jf9RV0GDHzybBlmLK0zXtZPckzXgw%2BiqtwxO3%2F%2B9MuuvcExQaNaRcUiNtAEyVSKxqYOSgvDqAUxmvbvFVUfUVD8QB6ablMFo6x6a%2FmBa%2B7eMrGli42qmsy1wa8zXICnlrf8R370kQuKi4ti1oqAIt1yEjBWsaJs0qCc%2BdBFMYCKilXIqPn0IejoTJrVHzUubOmiBeiZRvgzgPQJEJEwUEJw43RU6eNeiBPcAzSm8%2FN%2FRi9k%2FhckIlEgBKSO4fwxgnvcZj2afwF%2FhfF%2F%2BkeeoREnKEoAAAAASUVORK5CYII%3D',
move : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAWElEQVQ4y7VTWw4AMAQbh+2hXNZ+l21eEf4IpcpakwZAAaiXQ17x6YsIpQGsrhaICfqh8ADwp7PH+dkJR2NHFKlafO+ERzXPxLgqUSa3JGP7kNqn3H6mtm0hKkEgnsN7egAAAABJRU5ErkJggg==',
newTour : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAABmJLR0QA%2FADpAE8017ENAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1QISDiYc07YZKQAAAWpJREFUOMutkE9LW1EQxX9z741WJH0lgtDKWxbc1O5cZqkb8w2ycVm%2FRd6y3XcjBMSC38BNN90qhVJKslJKSVqEEP9FYpved8eFJsYYk1R6NjMMc%2BacM1Iul7drtVqREZid%2FcvcXJtn0W%2Fyy5%2Ff5aJvH%2BHPvmTDGaVSScfh%2FHhTL0%2FfaOe8oJcnK52LxuKutrLr2jIzrqvSbDYJIQAgIj31jNlj2n4h474iZgnrTCbY9mrHt1tTzld7BwbJ3WrkCGvqiHmFuAJgMSpG01%2BL4F66%2FqyD5OtebzoLTIE4EAuCABduNFkIOk%2FQ59hQRdNpQNC0ot6besamFffQ57uHAq%2Fx4SfgsVoBFO9f6OlZ69PM%2FPeDkRFEBOUpHV1DdYE0%2FQE8YWvn0GxsbL8FcKPItzWL1zwiQhRFNBpJT9QwIfp%2F0497EYY5eIh8x8GwpcHZsB0zqd2xEXK5HI%2BBi%2BP4Q5IkxX8hxXH8nv%2BFK8w9mWB7rBTJAAAAAElFTkSuQmCC',
openTour : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAABmJLR0QA%2FwD%2FAP%2BgvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH1QsVETINEBVQCwAAAaZJREFUOMulkb9PU1EUxz8vfajv0QYLDE1MNxlgc0ITIwODfwAIRgYkDBIQHNwMBAYXBxY2E34OBNQwA9FBRwYTEgxshGhMhCZtbeC95%2B279zi0aa15TTF%2BcpN7T8493%2FO951oPBvtmU6nUHH%2BhlCrtv9TYyvLaa%2BpgTT6bkJHh0chkEPgsrSxSKBQevXuztREpMP50TAb6Bzk8OgTAboqRSMRpRC6ffTn%2BZGrG1lrjOC7XW5IlxZjh4cBQQ4HNt%2BvTwIytlML3PfI%2FcwAkW1sA8LyLusWu21w522EY1jgAU0kelZ%2F1J52dXTWxDUQ6iLocxaUdxOMJ0ul0tIDve7z%2Fcs5BxgVg%2FsN6nX6fa6LeiSWxARzH5SDjsvD8PoHSXIZrV2JMze9WZwDwIxdwfHbRsFhrQ7rdrQ7RcUqBACKCZVmICCJgRNBGCLVQ1AYVGkIttCWu1v7CzaRP9lzxPeuDWBiE8kIEBEGkLAwERVMVOM2ccevOXfZP8uWiUncRMAiCBSIVV90drexu75AMvq1a9x6%2F%2Bug13ejhH2kuft37tPriNv%2FLbzdmyosZb3GLAAAAAElFTkSuQmCC',
pin : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89%2BbN%2FrXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz%2FSMBAPh%2BPDwrIsAHvgABeNMLCADATZvAMByH%2Fw%2FqQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf%2BbTAICd%2BJl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA%2Fg88wAAKCRFRHgg%2FP9eM4Ors7ONo62Dl8t6r8G%2FyJiYuP%2B5c%2BrcEAAAOF0ftH%2BLC%2BzGoA7BoBt%2FqIl7gRoXgugdfeLZrIPQLUAoOnaV%2FNw%2BH48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl%2FAV%2F1s%2BX48%2FPf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H%2FLcL%2F%2Fwd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s%2BwM%2B3zUAsGo%2BAXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93%2F%2B8%2F%2FUegJQCAZkmScQAAXkQkLlTKsz%2FHCAAARKCBKrBBG%2FTBGCzABhzBBdzBC%2FxgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD%2FphCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8%2BQ8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8%2BxdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR%2BcQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI%2BksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG%2BQh8lsKnWJAcaT4U%2BIoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr%2Bh0uhHdlR5Ol9BX0svpR%2BiX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK%2BYTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI%2BpXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q%2FpH5Z%2FYkGWcNMw09DpFGgsV%2FjvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY%2FR27iz2qqaE5QzNKM1ezUvOUZj8H45hx%2BJx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4%2FOBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up%2B6Ynr5egJ5Mb6feeb3n%2Bhx9L%2F1U%2FW36p%2FVHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm%2Beb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw%2B6TvZN9un2N%2FT0HDYfZDqsdWh1%2Bc7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc%2BLpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26%2FuNu5p7ofcn8w0nymeWTNz0MPIQ%2BBR5dE%2FC5%2BVMGvfrH5PQ0%2BBZ7XnIy9jL5FXrdewt6V3qvdh7xc%2B9j5yn%2BM%2B4zw33jLeWV%2FMN8C3yLfLT8Nvnl%2BF30N%2FI%2F9k%2F3r%2F0QCngCUBZwOJgUGBWwL7%2BHp8Ib%2BOPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo%2Bqi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt%2F87fOH4p3iC%2BN7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi%2FRNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z%2Bpn5mZ2y6xlhbL%2BxW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a%2FzYnKOZarnivN7cyzytuQN5zvn%2F%2FtEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1%2B1dT1gvWd%2B1YfqGnRs%2BFYmKrhTbF5cVf9go3HjlG4dvyr%2BZ3JS0qavEuWTPZtJm6ebeLZ5bDpaql%2BaXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO%2FPLi8ZafJzs07P1SkVPRU%2BlQ27tLdtWHX%2BG7R7ht7vPY07NXbW7z3%2FT7JvttVAVVN1WbVZftJ%2B7P3P66Jqun4lvttXa1ObXHtxwPSA%2F0HIw6217nU1R3SPVRSj9Yr60cOxx%2B%2B%2Fp3vdy0NNg1VjZzG4iNwRHnk6fcJ3%2FceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w%2B0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb%2B%2B6EHTh0kX%2Fi%2Bc7vDvOXPK4dPKy2%2BUTV7hXmq86X23qdOo8%2FpPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb%2F1tWeOT3dvfN6b%2FfF9%2FXfFt1%2Bcif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v%2B3Njv3H9qwHeg89HcR%2FcGhYPP%2FpH1jw9DBY%2BZj8uGDYbrnjg%2BOTniP3L96fynQ89kzyaeF%2F6i%2FsuuFxYvfvjV69fO0ZjRoZfyl5O%2FbXyl%2FerA6xmv28bCxh6%2ByXgzMV70VvvtwXfcdx3vo98PT%2BR8IH8o%2F2j5sfVT0Kf7kxmTk%2F8EA5jz%2FGMzLdsAAAAGYktHRAD%2FAP8A%2F6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfbBQcMJyOQosv2AAAD4UlEQVRYw8WXzU9cVRiHn3PuvXOH%2BWLK8FmhkDZltEAqdeHCnbGbmqgJqfEvcOMsTFyw6ca4mhgTE9HEhf9BiV8JqyZGo4AYDCpCIY0x6IAQmDIznZl758651wUXuWmwDJQpJ3lzc3PevOc5v%2Fc9X3DGTRzHeXAsG9E0%2BZ6uaa8p1015HniAEGLHVeq253nvrk6OV5oCMDiW7WwJh%2B4%2B%2B8xA4vLFPq2ORqFSp1Cpky9W%2BGd9QxV2NsuqXr%2B8Ojm%2B1WhcvVFHTZPLAz1t525ef45CVVGoOBiGg6bXkbqBGYlp24ab2NjY%2FBp4vtG4smFSTeb%2B%2BHubuYUVUlHJhY4Il7pjpJ%2BKMdIboVVts76xiRBi%2FjgpaFgBIdixanW%2B%2Bn6JL7%2F7HV3XEAKkAM%2F1qNoOSrkgZakpAHhsADiuIHVpiO4L%2FVzrj%2FFCH6hiiTezn6NJiXLd6nEAGk6Bo9wNKQ9qNhRuIRqL0tYaPQgmhQ0UmgKglLula7J%2BRJoU0BwFgLw%2Fw0esaeE2E6AoEMYRCoQA1RQAwzDeDptGKB4xD%2B1vT0aJtZghAR8OjmWTp7oKht744JNUZ%2BdQSNdIhBQXB2K0dxn0JjQcq8J2sUo8YrK5XcSQJBXiG2D0VLbiwbHs1Ugs9u2LN260SilZWfyNrfUcxUIJ8AjpGoYmcewauq9nte4VFXy0cnv81mMrEDaNt4bTA1Ep96Knh0cYuTZKe3sc6SpWfl3k55mf8ALJjIS0RM0TrwC3HrsGLNt5Z35hyes2ykTNPfdarU7%2Bfhl0jfTVYa6Mjvzn33EuxoXzKQdYbOiMOcphZ%2FlOre3plz69e2%2FtZVW%2BH073Js3z7XHCGkR0SUfCZHCgi85kC8loiJ2SXfhrq7hg1eo3d5bvOKd6H7jy%2BvvZSEvo1dIDKx2PHqyGUtnGNI2SFOLHilX7eHVy%2FIumXEj228TExG5rqqe15jjYtoVrP1jLZDL9J4klOVmbT0R0OpIRYnt1MXPSK9lJAeYcx8E0TTzPOxOAedu2MU0Tx3HOBGChWq1iGAaWZVWBhScKkMlk7lmWlbdtG2Auk8nUnrQCAPPlchlg7qSr6aitWPgblQyY2DfLshZLpdL13d3dX4A4e0%2BEh831j2fXt4YB9gcPAWH%2FawSBcrncn%2FF4nKmpqTWgJzCQB9R9s32rBaCOBWACUd%2FCPoQOyJmZmc2%2Bvr7c9PS0DnQFABTgABaw%2F0pSvnmN1oAX%2BD4sqQt4s7Oz6%2Fl8%2FofArIP9buCfwwZupAaUL5%2FwZxRMgQDk0tLSZ8B6YLBg3oMpUP8HIY4oQnlIERIoxsMU4xA13EepcKbtX%2BRcieZqbkRNAAAAAElFTkSuQmCC',
pinned : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89%2BbN%2FrXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz%2FSMBAPh%2BPDwrIsAHvgABeNMLCADATZvAMByH%2Fw%2FqQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf%2BbTAICd%2BJl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA%2Fg88wAAKCRFRHgg%2FP9eM4Ors7ONo62Dl8t6r8G%2FyJiYuP%2B5c%2BrcEAAAOF0ftH%2BLC%2BzGoA7BoBt%2FqIl7gRoXgugdfeLZrIPQLUAoOnaV%2FNw%2BH48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl%2FAV%2F1s%2BX48%2FPf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H%2FLcL%2F%2Fwd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s%2BwM%2B3zUAsGo%2BAXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93%2F%2B8%2F%2FUegJQCAZkmScQAAXkQkLlTKsz%2FHCAAARKCBKrBBG%2FTBGCzABhzBBdzBC%2FxgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD%2FphCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8%2BQ8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8%2BxdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR%2BcQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI%2BksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG%2BQh8lsKnWJAcaT4U%2BIoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr%2Bh0uhHdlR5Ol9BX0svpR%2BiX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK%2BYTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI%2BpXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q%2FpH5Z%2FYkGWcNMw09DpFGgsV%2FjvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY%2FR27iz2qqaE5QzNKM1ezUvOUZj8H45hx%2BJx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4%2FOBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up%2B6Ynr5egJ5Mb6feeb3n%2Bhx9L%2F1U%2FW36p%2FVHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm%2Beb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw%2B6TvZN9un2N%2FT0HDYfZDqsdWh1%2Bc7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc%2BLpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26%2FuNu5p7ofcn8w0nymeWTNz0MPIQ%2BBR5dE%2FC5%2BVMGvfrH5PQ0%2BBZ7XnIy9jL5FXrdewt6V3qvdh7xc%2B9j5yn%2BM%2B4zw33jLeWV%2FMN8C3yLfLT8Nvnl%2BF30N%2FI%2F9k%2F3r%2F0QCngCUBZwOJgUGBWwL7%2BHp8Ib%2BOPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo%2Bqi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt%2F87fOH4p3iC%2BN7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi%2FRNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z%2Bpn5mZ2y6xlhbL%2BxW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a%2FzYnKOZarnivN7cyzytuQN5zvn%2F%2FtEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1%2B1dT1gvWd%2B1YfqGnRs%2BFYmKrhTbF5cVf9go3HjlG4dvyr%2BZ3JS0qavEuWTPZtJm6ebeLZ5bDpaql%2BaXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO%2FPLi8ZafJzs07P1SkVPRU%2BlQ27tLdtWHX%2BG7R7ht7vPY07NXbW7z3%2FT7JvttVAVVN1WbVZftJ%2B7P3P66Jqun4lvttXa1ObXHtxwPSA%2F0HIw6217nU1R3SPVRSj9Yr60cOxx%2B%2B%2Fp3vdy0NNg1VjZzG4iNwRHnk6fcJ3%2FceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w%2B0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb%2B%2B6EHTh0kX%2Fi%2Bc7vDvOXPK4dPKy2%2BUTV7hXmq86X23qdOo8%2FpPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb%2F1tWeOT3dvfN6b%2FfF9%2FXfFt1%2Bcif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v%2B3Njv3H9qwHeg89HcR%2FcGhYPP%2FpH1jw9DBY%2BZj8uGDYbrnjg%2BOTniP3L96fynQ89kzyaeF%2F6i%2FsuuFxYvfvjV69fO0ZjRoZfyl5O%2FbXyl%2FerA6xmv28bCxh6%2ByXgzMV70VvvtwXfcdx3vo98PT%2BR8IH8o%2F2j5sfVT0Kf7kxmTk%2F8EA5jz%2FGMzLdsAAAAGYktHRAD%2FAP8A%2F6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfbBQcMJy13GubxAAADKElEQVRYw%2B2Wz4tbVRTHP%2Ffe915%2BvWTSJJNW7XTCDM0o08G2LlwIKoILuyozCP4F7irShbPppnSVhSAI%2FQNcSjv%2BWrhQFEWoWBTRtppBFH9M0kwzMyQxP9%2B797no04m2dDLYgGAOHC7cczn3e7%2Ffc857MLGJTWxiE%2Fu%2Fm9jP4eJKKa6UvGApdVobkw0CCAAhxJbR%2BlIQBOfXL692xgKguFLKx6LO98cfKaSOzs0oH0Wj49Po%2BGw3O9ysVHVjq9bWvn90%2FfLq5qh5rVEPKiW%2FKzyQOfD8s4%2FR6GoaHQ%2Fb9lCWj7RsInFX1W2TqlZr7wGPj5pXjoxUyY0ff6vzxddlsgnJkek484dcFh5yWTocZ0rXqVRrCCG%2B3I8EIzMgBFu9gc%2B7n93gnU%2BvY1kKIUAKCExAt%2B%2BhtQEpW2MBQEAVwDOC7Pwih47McnLW5YkZ0M0WL5beQkmJNqa7HwAjS%2BBpU5Vyt2adaIyEmyAzldhNJkUfaIwFgNZm01LS30MmDYyHAWA7fOE9elqYcQJoCoS9BwMOoMcCwLbtl6MR20nGI3eN59IJ3FjEEfBacaWUvq9dsPjCqxez%2BfyiYylSjmau4JI7aHM4pfB6HerNLsl4hFq9iS1Ja8THwIn7MoqLK6VH4677yTOnTk1JKSlf%2B5bNygbNRgsIcCyFrSRef4AV8tn1g6aG18uXVs%2F9GwYEoCK2OnNsoZCQ8nb2hWNLLJ08QS6XRBpN%2BZtrfHXlKsGQmHFHpfomOA1cCGvChH7niL%2FX5YAz6LavtmT2pSePF1RgR%2FF0gNYGzzfE3Qi5%2FDRGa2qVmwBMH3DJZpJ%2Bfafx%2Ba0bH73%2F1xi77SMDkCE7sd7Or86gtf32L83I0357J%2FLwTMZ%2BMJckqiBuSaZTEYqFg%2BTTMdIJh3qj%2B%2FvP1VvlHz68eFb3WiZk4E8WglFrQAAOEANcIAFE55975exUZvqpds%2FMJhO73dBq93Fs1Qn83vXtjfU3K1fe%2BADoA%2B0h9%2B4mg9hDAhuIhmDscE%2BGcbm8vDy3trb2U%2FgyM7RqwA9B9IFBuBfspwtEeNmwi6GY%2BNun6s7V%2FMOD%2F%2BQ%2F4R%2FRviSbeGCJRgAAAABJRU5ErkJggg%3D%3D',
plus : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFQSURBVDiNpZM7SwNBFIXPTB47TYioMAErLSz9BYJFIJ2FheA%2FEO2CTTB1go2ktrMSxFIQjFgIgkU6SxtthAyIGC0yz10LzSaz7AYlt7pzOXzcc7hDoijCLJVPG9aa5b5zIZ%2Bc5XJUdFuDyp8AzoW8tXvszZonBzxNmwoAAGMVnt%2FuAQDLi%2BtZsikAp2CdjfupgNphue%2FCsWcWBNIYxZx1PwCjwIJAVhulOPEcpaLbHlTyAODCkNd36pNgltxgb2ufAQB%2BEZ3zDvcsGKPw%2Bv6YuuaL6Hnvpfk13wIAaCNhrc30OlnayBSAlVgorXhC8fEEAOBzqz7AJgCUEnF6dRaHWCwU5PbG5jhELXFxdym1MWykoZSIGHDT%2FvQurNooRcqOQ1RWQRvDbo%2B%2BSNJO5h1oPcRoA62HWbJsgDJDsGI57v8FoISI694DT87StGTW7%2FwNezmaY41c7QEAAAAASUVORK5CYII%3D',
printer : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9oIGQopDsx58QcAAAHySURBVDjLpZNNaxNRFIafuWYRQRCLoCnTQIsfFAwxGFGQLBVRXPqJQfFHCEqd2iIupK6q7lwIfiVZ6EKrVkRGjQUDlRJQIVqoRd0Ixk7aTGbm3uuqTYbUVPDC4XIOnOe897xc%2BM9jtCb5Qm4IsP6hb%2FjI4aODbdV8IacbjUbHcF1X5ws5vdgTWQ5fKpX%2BOjqdTofyZQGJRKL5RsMI3VrrzgClFOVyGQAhRBsolUqtrMA0TYQQGIbRFisq0Fpj2%2FbSRCEEmUyGkddZ6t4co8cn2m28YA0MAVY0GmXL5q0opULyAZ7PX6W7q5cP3yZJ%2Fsriui7A8KICa9C6iJQS3%2FcBOP9wP1oFeCrAkwHd6zbRH9uF487zsj7K7TNTXL9xzYq0LqlSqRCLxbBtm0B57N12CqkVUkkUmu%2FVWRI9e6h5dQ7ejHOAs80dOI6DW69TrVZJJpM8sj2kVsz8rOCrgED5%2BNJnruGwvSdDzV%2Fg7vSlJkAIwZzjMD7%2BjFqthtvrEsiADWvjBFIiteLH7690rdnI5GyR4uci2VUDYReklOxI7wTg45cct96O4CkPN%2FDpW9%2FP7r59lGbe8OLTK3KHJhh78rgJ8DwP0zSX7LscHwu5cOJBGkOs5t30FE9PvkcpFbLxHnCs0%2Fe7o67gK8npyLnW8v0%2F5Gb7fMJoZowAAAAASUVORK5CYII%3D',
refresh : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTAw9HKhAAAAyElEQVQ4T72TbQ+CIBSF67e3rFbO1GhiDNGROntxzfrSn7uJm2VCshaL7Xw5ynPPvcAYAEZGlgCZkBFI05WJNFrQLr8CSkrg5b3+9zUC4fUDKBOFaQU+O0lCyfnpaUGYX8ClB6liVNwav5UW5JBcgohNwu9qEBTUvds4lUDC62sQZOM9rAKuTKQ73bdhI3aEOYp/B4mqlkfNgJbbGKZrooR5JPtYRHmPZhsKEyeUtEDRd6B2sD7NwHIJsKLStvuft6Y78u53Y4keAkWNbhTM7xIAAAAASUVORK5CYII=',
save : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9sDHAwxAVECGvsAAAJVSURBVDjLpZBNS1RRGICfO15HZ1BsphDHclHYLoXA0NBqkdQu6EMNEvoiSDI37SpJaBERbSRCyMrIXT%2FAjLKwUMfEFMMPJL8tU9FRcu54557zthgdKTKIHg6czXue8%2FAap0pP3AoEAjX8hm3bsTuyejm0732dx0wi2Uwm2XRjutzUln8wAMxAIFBz%2FuxF%2FkQkYvH4aX1dxLYpy6vCTDBJMFy0fH4en3EppVheXqIj2E5HsJ2u7k6GhvsZGu5nfHKU4uLD2NrG0Yrx%2BWGUaGzt8LCu9jaAqZTC4%2FGyJc0HgJGgOV165peSB%2FcqcZQiqh2UVtjKwefz3wSqTdu2sawwoaVFAHz%2BNADC4RVy7vhJ8yayJyMXJRpHR9EImb5s3i4%2F4GrjQXE5jhMvWK9YJ6oVOZn5FOw6wrelCaIqykxoitysIjL9O7HsZVxAvGC9Yp3BG0t0jvTSNdGGPyUDl8uNLyWdztEW%2Bqe7qb%2FQY%2Fy1YGCgn8ZjTbwZbOXTZBvpqTto%2F%2FKa3qkgx%2F3VsZ2VlJ2UKxWVPHs1TN%2Bcl82YTy%2BnMLuQwa8fMcaeAJDmXsUE8Hi89M15qb12lIitNlHMcOlFBo9KZgBIdidQdb85JrCscGxkMcLI7MqmFRV5g3SPhlBKk7UtVhsvABBARDAMAxFBBLQISguOEqJKYzsaRwlbU5M2BJYVJttnsfDDZnrBAjHQCGsHERAEkTUxEInqDcH3uVn27i%2BiZyy09ij2uwhoBMEAkXhV%2Fm4%2FzU0v8YUnGowD5%2B6%2BsxK3H%2BIf8UYngq0N1wv4X34Ck8Uv%2BymvOfsAAAAASUVORK5CYII%3D',
send2cgeo : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AwZFBk3CuNDjQAAArlJREFUOMudk0tMlGcUhp/v//+ZYaDMKNQRSAcL6iBiS9MYbWy5aFK7VRfujFHX7kzbtCqZBSZtohsTFybddNW4oDZNSEvTNiDTi5naoIkC441BBBxu0xlm/tv3fW6kQejGvrtzkvPkPW/yimQyqfkf6unpEQAWQM3BG1USPnKlOGBLcdRRxDwFrgL5Am8KMR22xCcbauQ1+/oH9grIAogEzbijdJ+vFAFDoDVgCAQaBQgBpkG9s+x/nStUjlavcmIAjIz9tBiRMTYEKzCFImBC0NBUWIIKS4AtmcnmmRnXfHvp5qbVr1gAn+6fSM8WxvWsPSBMcZ+ck8cUBl7ZY2mhhFeM0ll3DB2MLO856pzJ56ZeduCPXY5bpZDY2XCM9kg3icomQssl1HyA3dHjnNp7kY5dhyhnhqo6o6n96xxoZ5Hsn1dpqb3I5uouxiczbA2eZHt7N1q4ZLMT/Pr9BULPfqF66z7WAUKVEcKlQQa+6eXIiR46dpwnNzfL0+kMvw/2Mz/Sh3+okbwbIxAKrwcQ3kLi7U7m/vibH/q+ouaNBGO3h1l4cotEg0nru438qH3q69r4eCpNG4k1AGcOig/YvUWRetDP8J0hinsW0A0+w8rHlT4N0W201u+lYC9zo3xlTQblKVRxAsol9jVV0toW42pphg93HUdqhVQShebp0iRvxd+n6JZZ7B30//rctSwAq7Ydo+pNAkaE0MY4jbWbsL87jdSKibkMnvLxlYcnPf5xCrwT76Dolcylc6mCBTD0KHZtPJN/dud2av7uvdFCy44WU3fZX/jSZ3O0EV9KpFZM57PUvFbHrckUqfup4uNeqkUymdQrxVitw1eatO26uMrF9j2aX2/lveaDpLO/8fPoUCl7wan6N4P/bGTu5fH6zJcII8zNhyP2yvErqflsMB3/zEyv3T8H2P86Vd9nqzEAAAAASUVORK5CYII=',
sendGPS : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9oIGQooOW3fZUkAAAFtSURBVDjLrZPLK0RhGMZ%2Fx62UEimSo0wihSxphhXKwsLGdsrextKt2fAnsJGVlX%2FAwqXIIRPCSpHLjEjJdcJ857vYOBhzjhRPfX3f4n1%2Bve%2FT%2B1l8ynx5W%2FxSOfxRQQDzHx38CmJ9L3aTXQDk24tBtf4Az%2Bind5gvIM%2BnMEM%2FgbMAALHxGJG2MACdPZ%2FmwbkO8yIemRnYs34M0TN%2FV1oKqsoa6J%2BuN4EZ5NuLxMZjANzWLWG0RGiJUJLKklqa7Qjx0xX2L7ZYGrq2fEeItIVZ33SQWtDVGEUZjdIKjeHyPkmTHSYlXribWDU7I8LKAnhzD8y2oIzm%2FOYIV0ukdnGVy2P6iRa7nZT7zP2YY%2FKCEo8uJJFKUl5cjVQKZTRXDwlKiyrYTTo4xw5nE1hZGXh331QNr0IgtOBVuoTKGmgNdbOd2GD5cI3EZDozA8%2Fsab63NmM3nicPsHIKiZ%2Fsf5j9dj%2FoEBotMPZwbtb%2FeAM%2Bw7BUpUnjdQAAAABJRU5ErkJggg%3D%3D',
saveAsBookmarklist: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABwklEQVQ4y52TzUtUURiHnzuFEIRRDirZBIkbwYlAqMAP2hj6D7RoE+YqIReBq2g5s6lFOylqIelmNi5zYcZEEzSRZKs+BqeZyUSYTJ1h7txzz4eb43C5TYP0g5fzct7Dc37nnPc4wDgwCeT5t8Zs7DQrjttopafAc6AjXIhwNO0As8BDIPo/gAvAZWAFWAo6OQb02TxnRwdoB7wAoGpHF6jZNTmA46GdbgJDwCIQtwsPlQXWwtbCgCyQBjZtBAG/m50tDMjZY3UChWDh7sKoccU+z25/mmgFmAKuAE9sbzQceFJwLtrPjTn3ZerO1wbECfTAMtBjL688szBsjJYILRFKcvZ0Hxdjw2Tzq6z/fM/KvW2nmYNNoA2ISS0YG7iFMhqlFRrDr90S8dgQVeHyJ5E2H+8LJwyYBgaBx3UpUEZTKH/H1xKpfXzls+9VuBQboerX2H2QMWFACngBVOqyjlSSrlPnkUqhjGZrr8iZk92slTJkchl+JPjLQfkwcaXH/LtHCC2oS5/eaD9Xe6/zofCWV1/eUEzS9A4aWprOB3uAWvKzcSInyG6sU0x6E+FnnASutfoMezWf19/SlJIqFZw/AMBssOgBCdZnAAAAAElFTkSuQmCC',
settings : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAABmJLR0QA%2FwD%2FAP%2BgvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1QkaDBM5i2PCSAAAAfBJREFUOMulkktoE2EUhb%2BZ%2BEyKTRQKgkqwzMaFtt1FrC40FGJm60JwIVSkqLUtElICFQNDQqBrQXRlQIriwomN0GJXgtI2iUkXFYJVadOXhiBERDozbmaGMR3rwrP7ueece%2B%2B5P%2FwnBOcjnVGigArI8Vgi9xdNNJ1RbI7YUlT7r%2FYDqKaZq%2Fj6tQHNbLQd6YxiNBp1I51RDPdaw6pFAcR0RolaZKur19vmZhwFePDwPvFYQgZyACKgDt4cMp4%2BmzAA9fatETbX15A6Jer1r%2Fdas4ndGRUsMYBgFW8MDBqatiXoum7oukZhfk4ovC8CyDsFK7R0sBHpu0i5UmG59gUgGY8l7v7zjE68yr80SpUS3Sd7KJYLmBNMArqrQTCSOgzUrPeVkE7XCYmjR47RbDZ5N%2FcWtzU8TvH4cJi%2BUCcdAS%2FZmU2Ot39LLn1eOtd9qoeAP8BKbfnyhfD5%2Bemp11XAABCDkVQXUHs0JjNbXmS2vEjHQR8A5t5yLv8CSZI4e7rX%2BmR2HiJQHB8OM%2FWmxJamI%2B7zs1Fv2iOaI8vZJ4850O7nTKgXYMxpAMDuXR72%2BA7x88cvsvkFgHCrSS6vUv1Y%2FSNsEWBl4zv7fQHa9np4PvMBIPxpcnTaSTRNkmvrqwtA0r5CMJK6BEw4uNvEO%2BE3N%2BLV9uq8VLwAAAAASUVORK5CYII%3D',
sort : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABd0lEQVQ4y43RP2sUYRAG8N+stwoKNmIXERRMCislgsKBxRVio9aJAdEigh/B3tpK0S8gWIiFIChY2CYKVsYyWNkKF3CjY3Hvm6zHXXBgi9nnzzvzTIxGVxskIqJdwjCze4YwqYrdxElsZnabBbMnLv0QT2eIT+BlwR7Zr2h65OmqxomV8thbjCLaUxVr/kMMt/EZD0p/p3KanqBfe+KI9jwuYAEv8LsYxqwMzDBcwR9sF943nI1or1SDmDNFrbt4k9ldyuyWcRk7WIPBnJ2V8Y/iGr5XTmb3M6K9WDmDKfE/q2R244jBFrEY0S70vTO7T9UgDhg/iEVszMAO1TMeIJbkFpbL97xg76bPOJ1+7O+8Oy7jHsc9fMGtyhmUsIZYxVLpn0ySzoeZu+OI9jRelzBvZHY79bGmhPWx/BiWCVbxaiIeHMP7svMa+SNicKR/hZr4/Yi2iq8XU8Q5nCm0D71DNX2DQGb+Wo84/Diz+9rLZRvrc4L2Fz6qhgNKqYn8AAAAAElFTkSuQmCC',
startAutoTour : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAAAcCAYAAAAZSVOEAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKTgAACk4BGCrFqwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAiJSURBVGiB7Zl%2FjFxVFcc%2F571582t3ZnZ39vcWWnZpKW3X8qMtCogCIhUBoURChEIUAYEGY43aAEIwEPwRqbEICpGQ4h8qqIhGBBpsEEpLoKTQ1ha32x%2B7bEt32w77a3Zm3rvHP2aWvp3OtrNlIUH4JjeZe%2B455577nfvOvec9UVU%2BQWn86ZbELeHaaXd1dfVc%2Bq2He1ePp2d9iDF95GAFrGnzF95cdc4l1z%2B98tutP0NESupNxmQislREtvhacDL8HkUc04riuGIi9vddITMeWTLl1se%2Bc9wDABgv446kSAQz4fMWfXPpE7fPe2XF5VJXbBeYpPjrgRN8%2FZL%2F2IeAYFEc1YdT%2FsViaYpG6y%2BOV9ctSh479%2Fjzr7u%2Frv6Y2bGOl1buAW7yVEc818U2WUx%2Fp%2FXpz5w%2Bb0tNzYYHb6q%2F9sYH9j496meySPzIYeV3p6%2BeftrVsxqPOylZ3dBqWXYARfHUAvXif7jz9E3hxNQ4eACIWLjpPlqb4k3x6GmP3%2F%2BN5GVLHtn3DHxMSVxxlcyae9bX2rvSNTUDO3uJ9I4QilQSikSpq4dgwI6e%2FNnLZlXYwwz1dhBubMN4exDLBpRE2KuoqU4sAZ6BMnKiiIREZLaIJCZzIZJHm4i0vE8%2FSRFpmohNNFF3fTzZUoPnUulkiIVcYmFDLCyEg4JmD5Deuxl3aA%2B2pfRsepbBkSy2E0EsG8uyqK%2Btnjt60IxLooicICLPAkPARuCAiGwVkQt8Op8TkR3AjUXmW0Vkh4i0lvB7rYi8BPQDHUC3iOwVkb%2BKSLOILCjYjrZwwe48n2yriLSIyOtAH3CNiLwCPF803d0F%2FR%2F4heFQ6MyhdBbHAStUTSBaSyTRTLS6GZM%2BgJsZIBRNUFFZQ6J2Ki0nno2bHmB3Txc5qUQsm7ramsYVVzqnwTiPs4i0A%2BuAiF8MzAD%2BLiLLVPWnhfGpJVyMyhyfz0bgYeDCEvp1wMXAKcBPinyOHlJRn9wDVgPH%2B%2FSmAMU7ssbXAFh%2BubScePKC5v0DORwngpdNMdDdQf%2BWbnKDe7CClcRqmollBjBZ7z1H4cokqRGHjs7tWOEqjquOOPGa5A3A2vFy4krGEuiHAPeIyFPjjB9qIBIAngXaj6A6BfhlGS5txhJYNqJV8UVtx5%2FQ2N3XB54LBqxQBURasZpmEolUUBmLU1kZw4lGUQH1XILpYZLBdwnF6uhP7cOLVlCb3DEPSuzEQo46ySd6GrgZuBT4eUEWAM4FngS%2BDnwVuMBncx3gArsL%2Fe8xlsBh4E7guYLOp4BbgIuY2PXoDWBFwU8n0Arc6xtfCfwLeHNUYEugLuRY0tYQAkKIJfnUJgaxDNgZxDoAI0MMZwKICKoG4%2BWIkCUUyVAdCCCSIxQOx0fJKEZxHntZVbeLyHLgJd8i31bVt4FHRWQmY0l8TFUzkE%2F8wB2%2BsUFggar%2BxydbBawSkR8DY%2FLXYbAVmK%2Bq2UJ%2Fp4jMYCyJa1X10WJDNQbURY3B5DzU5JvxXN56Yye9qRFyQ2kCToD2eS2EQwKeh6ceeC7G87DDcbKZdA5Kk7gVUA6SdZuIRIE%2Fquq6MhfoxylA2Nf%2FURGBftwBLAKml%2BH3IR%2BBE4NYGPcgeWo8jHqsX%2FtfrOo4ba0hqmuaeWXdTlY%2F8SpespqF5xyD6kFdC0F1nNNZVfcCv%2FWJQsAyYL2IvCgiF8k4NeQ4KM6DfxlPsUDK38r0u30CMYydB0GNixoXY1x61m%2Bmq2M38eYkc1rjrP13J1nXZf6CZs5d1E5%2FKs07B9IYz8V4Luq5AIyyMN4V5yZgCdBbJD8DeAp4XkScQ6xKw1%2BGueRz1%2BGwuUy%2F6TL1SkAwxsMYj5HuHrq6U3TuPMC0xjDqucyeXc%2BWbfvo27aHTWu20RrOsmv3oI94D%2F%2FLr5IkqmpOVX8FtAH3AKkilc8DPywz4m7f7wBQewT993X5LgtioYVd1bvrHax4BdmRLJn9KYbWbmDX2yn2buwi1b2P3ZEEzS0x6Op9j%2Fj3iCTP5GErFlUdUNXbCwtbWjS8sMyQNxT1zzqC%2Fnll%2Bj1qqFgFMjyGIjGah%2FdjeS6rXtjJk1szJDVLLBFm34DLuVOFL9l9BIfSZLO5%2FCNtXNTL5g8oSpAoIneJyD8L7XERCarqsKouB172qTaXGfPrgP%2FN730iUlVKUUQuAc4s0%2B9RQ2wbUYOitM1q4i27ii84fSycarH48hM5NuSSaIhR40BbTxe7BmDlDodgMIgVcLDtICIWaowFpU%2Fnd4Dzff0OEbmX%2FInpPyReLSdgVe0SkYeAGwqiY4ANIrIUeAHYR%2F7ifBVw60TIOFqIhFCxsAC1lDPPnsH6znp612xk0eAbtDmG2ZlhYg2GzgF4rVdYfMEUHCeEUYMaDzsQALFsEYmWIvHP5C%2Bwo7t0GfB9Dt21v59A3MuArwCNhf6xwBOF3zl85eEHjUxOg5btYFkBjBosFYwqc9uSDNbO5%2FFVm5gjKbb1K9vSDsNOhFMTWbZ3j3DyrHosFNRgOxHUpAJA1SEkquqews67zSc%2BhEBVLZtEVU2JyIXA74CZRcN%2BAvcDDxbNPalwjQZAEUuwsVEVLMDyPDIb32LO9CQvdlVx6hebaQ3aNFWHAKU95%2BIE5eBh4oRQy7KBcMnaWVVvF5GN5Ms8f%2B4bLdeWTzR4VX1NRE4B7gauBBr8awPWANeQ36UfIIluP8bDqWhA8UBBUIwa3p05n%2Bb6Sq4%2BwyH%2FAc8UsrkSVs3nUQGMQSwb9VwDIEf62leopecCPcDmo64SDvXbWPC7H3hTVUcmw%2B%2BRcE67NFz%2F5RlrGmrrKgyIZRRPEEERLZSElqJGUdX8hVo1T7aQ34kKOdfV59Z1%2Fmb5PwZ%2FfUQS%2Fx9ReImbf0199IiSz%2Be9H1cSLfJ53rwPN0HAqGr2Y0niZOOTj%2FeTgE9InAT8D1d5WQk3kn0aAAAAAElFTkSuQmCC',
tabBg : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEcAAABWCAYAAACdOoshAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oKAwcSHqDAeZMAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAArElEQVR42u3XMQ5AUBRFwU9ESEgIlmn%2F5bMBt9Ap5ixhutNVVTXpa11V3RjeG1prF4aMc2LIOAeGjLNjyDgbhoyzYsg4C4aMM2PIOBOGjDNiyPtgPEM9Ajhw4MCBAwcOHDiCAwcOHDhw4MCBA0dw4MCBAwcOHDhwBAcOHDhw4MCBAweO4MCBAwcOHDhw4AgOHDhw4MCBAwcOHMGBAwcOHDhw4MARHDhw4MD5TQ9jZAyriwnP2QAAAABJRU5ErkJggg%3D%3D',
topArrow : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAOCAMAAADKSsaaAAAAAXNSR0IArs4c6QAAAKtQTFRFOwAWEnMAFXUAIncAHXoDHnoHIHsAInwGI30AKH4TK4AAM4UGMYYLO4oQQ44VRI4XSZAbUZYhVpcnWpooYZ0wXZ45Yp4wW587ZJ4wXqBCZ6A0ZqA4YaJFaqI2baI4ZaRHZ6RFbaQ%2BbqQ8c6pOe65UgLJZf7JggbRmhLVlhLVpiLdqjbluj7tzlcB%2FlsGBl8GCncSHoMWJoMeJpMiMqMqPp8uPqcuQrM2Tr86Vse9UEgAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsRAAALEQF%2FZF%2BRAAAAB3RJTUUH2ggZChAKxZC4pAAAAF9JREFUCNdjYAACDgYoYBPnhjBY5fUV%2BEEMThk9dSNlIQYGFkldJg5mM1UJBnYeRqA6LmlFXOo01XQg6kQNNExVwOp4DbXMRMAm8xlrm0uBWQIm5hZyYJagsJisEgMDAC4YCUlXya0PAAAAAElFTkSuQmCC',
upArrow : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAOCAMAAADKSsaaAAAAAXNSR0IArs4c6QAAAKhQTFRFEnMAFXUAIncAHXoDHnoHIHsAInwGI30AKH4TK4AAM4UGMYYLO4oQQ44VRI4XSZAbUZYhVpcnWpooYZ0wXZ45Yp4wW587ZJ4wXqBCZ6A0ZqA4YaJFaqI2baI4ZaRHZ6RFbaQ%2BbqQ8c6pOe65UgLJZf7JggbRmhLVlhLVpiLdqjbluj7tzlcB%2FlsGBl8GCncSHoMWJoMeJpMiMqMqPp8uPqcuQrM2Tr86VayLUTgAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsRAAALEQF%2FZF%2BRAAAAB3RJTUUH2ggZCiUstaz%2F7wAAAFNJREFUCNdjYCABsMMYrGJcEAaLnJ48H4jBIa2rZqgkyMDALKGjoaptqiLOwMYtoq9uoiylAJTmMdA0FQbr4DXSMpMEs%2FiNzcxlwSwBIVEZRQYGABF%2FBmSnRdN1AAAAAElFTkSuQmCC',
update : ' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFMSURBVEhLzZUhU8RADEbrkEgkkp+ARCKRSCQSicQhTyKRSCQSiUQikUgkFr7HNTffLUnZMhW8mTfTa7O73WyTG/4Lu/LIPJAZPNtZX/ZxKO/lZ+GjZFKIuOfvXx1cy3bCShby35PsyyfpA/BdMhG+jvcqS8gfW/TgW8miLXtyJT+kx2OJp4U3PpYZvAgL+6RuCoflQdXkcCY9tjXFD+qGGxOQnuoc0rEM8CC++0UhHTE5b7Y4nlMKZnHOZSzA1/FX4uu6k6R9w4mMBbpLPeFCxjxcb6B5xQOc1bAML9KtBeBFxkNSNhcaY4zHH9Xv50D5Vy05gx17XZR19CAjiO1uHVQBNeMt/W28l8KE9KAI5ppPuIL6YcKIx1M5CQFth2T7vOXVKNdZq+BZF+Q/+0+o/G2nJZfSU9bKTimsnrOahEKM9ITkf/GmOJNh+AI05q6HzluWjgAAAABJRU5ErkJggg==',
upload : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB9oIGQoqEWpcrzEAAAMVSURBVDjLlZPfa1t1AMU%2F33tvE5pmSZvfSZfWXlNnF7sfsrjqnqq4OUUQRFHZkJWhD4ooDIXpg4gWhPkH%2BCBWHJtPUwYyGSpspdN1raOyYqWlW5N2bZqmTdsk9%2Fbm3vv1ycn65ufxcM55OHAE23jl%2FaE3gTdUTdstwStdtyYlo4qqnD03eOzr7X5xL%2FjBN%2Fs8qvJTd1ci3renk672MD6vl7WqwUyhxPhkgen88g0hxMlzg8f%2BvK%2Fg5fe%2ByrUEmq%2B%2F0L9X7O2OsVY1KN3No%2Flj0NwG9RLt8TCjkwtcGp4sI8ST%2F5ZoAOGw%2F%2BfDh7KiVw%2BxurJEpCNLMJKkbjZYKsyiuRaKY%2FBUTsdxZfjytb%2B%2BBB4HUAY%2B%2FPbTRKwt0J%2FL0KS42JofFEGTpuFp0vB5NeLhHSRjUaKtLeSyO%2BlMhfpeO332BIASCPpOHsh2UFlbobjlI5Z6ACnBlYBQEK7F4mqNH67c4vLwOEGvZHcmBfASgLZZb0Q7E22MTi%2Bjd8dw5X%2FLViurPLYvixACKSULi0VMwyAZDQIcANBMy1a8Xg%2BKP0alauK4Er9XxXEcrGoJISIAvHu%2BH8Pa4Nn4GaLpXQAhAEVVFXe9ZtJizuM38iyX1rhT3OB2cZO5so3VaLBZrbFlW%2ByM9PBd%2Fi1qhgWwCqA1KcrS7fmV1P5MAj2dZK6wwPDMBmOLA0jX5tfzNpZjk2rL0JM8yKZZ48zIE8T4YgxAk45z4ebU%2FNtZPQZAZ7qdwI51fl%2BwePqR13Gki%2BM6uEjuVgr0pg9RtQzGjIGjAOrNqxcuZXLPnxKq6gm2NNHsUflt%2FBYTqxfJxPcztzJNuVaiXC1St6psmBs8GO2lbhvU9xQ%2B1gAqyyvHr9xwv3ddeHRXnB8n1jF9JrZjEw92YDsOjnRZXM8T8if4ozDCyMwIdz5D3PvCcyc%2Bf7VDTw8Fgq2eh%2FUEF%2FMvYloWlmth2g30SA99%2BmHG8tf4Zeoq%2BcEtcd%2BZAFpDXZ4jx08NqV7fM6haKwghpWtLuzH7d%2Bidh3pTBxmdnWDqk7Lg%2F6J%2F5JHp06rcrv8D001PzAwk7SYAAAAASUVORK5CYII%3D',
userscript : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALZSURBVBgZBcFLiFVlAADg7zzuPLzjzDjOMINMitIie5gF+UAkIZSgRQuXLZIWrY021dYIggJdJURElJsoqlWRYA9GshGFCNQeOjoTk6bjeOd5zzn/f07flzRNA459ObcHJ3cM9+1fq2prVa2qa+uh7mAZ9xCxiAV8iu9zgDqEvU9ODOx//dkxALBa1kNrZT202I2TZcVyEd28t+Lb66uHcTwHqEMYH+xJwNyDqJUk8oQsp7eV2tqbytJUK+OpyX5bhtojH07Pv58CxKoabOeEmuUy0al4UNDp0umysM5/KxG8eWbW/u1tj4+2xnKAWFUjG3tSqwWr3ShNEzmyjDQjk8gSaiRxyYUbiy7PduZzgFiW40P9mc56sFY00rSRpaQxkaVkGlmGJnNnqXDq7N9LOJYDhLLcNj7Y0uk2AjRkMZE2iGQaeZOqG2IrCmXY/s1rB+6nALEstk0M9VotG0lKliRSpEjw+YUjPjq3RxkKoSjEsoiQwvMnvusXQ09vK1VGUg1qjVrUqDWKUJoc3emVj3dbWeuEUJZLkEMoyrF2u0+aUEPD19OHNXVQ1kEZgy2bHrZzYq/l7qr766/m3VC0ub+SQyyLDXm7R56SpYlYJ0JdOvzYy2JTi3VUa8x35jwxecBKue7S7E+dXW+nI/nB42dGcWLPI1vdXmrcvBO1++iGUmxqtxb+UtVBqCtVrCwVy3Y/dNBKtZb+OjO1kMeyfA4vXLo6Y3E9t1I0qtjo6goxGB/cKtRRbGr/dmaNDEy4PHfe+etTd8vgSB6r6ukXD+3qf+ulfQDg6OnCJ7+8p6xL3VDaMfqofTuOuHhryrk/fl4tokPz7zRX8lhVM7fvdXx29qrhgX7Dg32G271OHv3dxg09entSvXnqmXcHJGm/6Ru/ad89dmrm9AdXIK9D+GLq4rXJqYvXtmEzNmMTNmGor6fV6utr6YxWfvjzR0P/vDGTh7GvAP4H2uh1wse2x/0AAAAASUVORK5CYII%3D',
add_comment : GS_HOST + 'images/stockholm/16x16/add_comment.gif',
attribute_blank : GS_HOST + 'images/attributes/attribute-blank.png',
bearing : GS_HOST + 'images/icons/compass/###BEARING###.gif',
cache_size : GS_HOST + 'images/icons/container/###SIZE###.gif',
coord_update : GS_HOST + 'images/icons/coord_update.gif',
fav : GS_HOST + 'images/icons/icon_fav.png',
stars_d : GS_HOST + 'images/stars/stars###DIFFICULTY###.gif',
stars_t : GS_HOST + 'images/stars/stars###TERRAIN###.gif',
tb_coin : GS_HOST + GS_WPT_IMAGE_PATH + 'tb_coin.gif',
wpt_type : GS_HOST + GS_WPT_IMAGE_PATH + '###TYPE###.gif',
sad : HTTP + '//forums.geocaching.com/GC/public/style_emoticons/default/sad.gif'
// not used
//closedHand: 'data:image/x-icon;base64,AAACAAEAICACAAcABQAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAEAAAAAAAAAAAAAAgAAAAAAAAAAAAAA%2F%2F%2F%2FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAA%2FAAAAfwAAAP%2BAAAH%2FgAAB%2F8AAAH%2FAAAB%2FwAAA%2F0AAANsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FgH%2F%2F%2F4B%2F%2F%2F8Af%2F%2F%2BAD%2F%2F%2FAA%2F%2F%2FwAH%2F%2F%2BAB%2F%2F%2FwAf%2F%2F4AH%2F%2F%2BAD%2F%2F%2FyT%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8%3D',
//mail: 'data:image/gif,GIF89a%0F%00%0D%00%C6f%00PR%A4SS%9Dae%BAdh%B8nl%AAwv%B3uw%C2%7B%7D%CB~~%BC%7B~%CE%82%81%BE%7F%81%C8%87%87%C3%8B%8B%CA%8A%8C%D1%8C%8D%C6%90%8E%CF%90%8F%CD%8F%90%D5%8F%92%CF%91%94%D5%90%96%CF%93%96%D7%97%98%DE%97%99%D9%9D%A0%DC%A8%A7%D5%AE%B0%E4%A9%B2%EC%B3%B2%DC%B1%B3%E8%B0%B5%E3%AC%B6%E5%B7%B6%E4%B7%B9%E3%B7%B9%E4%B9%B9%E3%B9%BC%EC%BC%BE%EF%BF%BF%E8%C0%C3%F0%C4%C3%E9%C2%C4%EE%BF%C4%F8%C0%C6%F9%C7%C8%F6%C3%CB%F6%CD%CC%F3%CF%CE%F0%CE%CF%F6%CF%D0%F7%D2%D1%EF%D2%D1%F4%CB%D3%FC%D3%D3%EF%D3%D3%F8%D4%D4%F7%CF%D6%FC%D6%D5%F3%D5%D6%F4%D7%D6%F5%D7%D7%F8%D5%D8%FA%D9%D9%F4%D6%D9%FD%D9%DB%FA%DD%DD%F5%DA%DD%FE%DA%DD%FF%DD%E0%FA%DE%E0%FD%DF%E0%FC%E1%E2%FF%E4%E4%F8%E3%E4%FE%E5%E5%FF%E6%E6%FA%E6%E7%FA%E7%E7%FB%E8%E8%FA%E8%E8%FF%E9%E8%FE%EA%EB%FE%EC%EC%FF%ED%ED%FE%ED%ED%FF%ED%EE%FE%EE%EE%FE%F0%F0%FE%F0%F0%FF%F1%F1%FF%F2%F2%FE%F2%F2%FF%F2%F3%FD%F3%F3%FE%F3%F3%FF%F4%F4%FF%F5%F5%FE%F5%F5%FF%F6%F6%FE%F8%F8%FD%F9%F9%FE%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%FE%11Created%20with%20GIMP%00!%F9%04%01%0A%00%7F%00%2C%00%00%00%00%0F%00%0D%00%00%07%85%80%7F%82%83%84%85%86%83%0E%17%09%87%83%12%1E1%2B%1C%03%86%06%18%25AR2%2C5%14%84%0B%19(FVbL-%40C%26%93%7F%0F%22%3BM%5Ded47JKG%16%82%15%20%1F%23)30%2FQSUW*%02%7F%13.9%3E%3D%3C%3ANY%5C_%5EE%07%82%1BDHPTX_bcO\'%0D%00%82%108PUZ%60aI%24%0C%01%85!U%5B%5BB%1D%08%8C%11%3F6%1A%05%8C%83%0A%08%FC%0B%04%00%3B',
//preview: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAAB4CAYAAAAKVry3AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKbAAACmwB9fwntgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7L15jKRnft%2F3%2FT3P895v3X0fczSHQ84MzyV3V1RE7W68sODE3iQOZq1AkJHDIATIGyQIEgQI4plBBERBpChZO3KWsOFAtuCEYzgRvJEcaANRkiWvY2kpc3nskHP23V131Xu%2Fz5E%2Fqqs5wz14zCx3FqkP8KKqp4vset%2B369u%2F%2B0fGGMyYMWPGD4L9qN%2FAjBkzHn5mQjFjxowPZCYUM2bM%2BEBmQjFjxowPRPyo38BHgYju%2BfL7ve7y5csAgEuXLhkiwixgO2PG%2FUE%2FLh%2BiK1euULPZpDfeeIPeeadPQJuCIECv18NoNDp6VRXVahXN0y2shqERQmBhYUEDMJcuXfrxONEZMx5CHmqhICK88sorFMcx7e3tidFoZG1ubvLt7Q6XUgrNBIujlJIkgVIKnm%2BjWqmiWQtMELg6rFdks1otHMcpHceR58%2BfN1%2F%2B8pfNw3zOM2Y8jDzUQnHlyhU2GAx4UZCdq8IrktzfP%2Bw5%2B%2B2BJSW3DRgvckNpKaGVhutwVEIbtdA2FV8oP7Rz33UTxigGeLq6Olc6jiMxszBmzPhIPHRCMbUi3nrrLXbz5r6VpokXp3kYp0WlKHk4HBt7EJEolCM0bKYUI6UJMARLGFR8Qj00phZo7bmy5JSnWuWx4%2FCo2awknPMky7J8Z2dHvfrqqzPrYsaMD8FDFcwkIly%2BfJlu3rzJDw8H7mDQD9v9pDYcm0qc2X6uA6dQNZ6qOlM6JEMODAQMYyAAJQNgDAQUHFZAYGgp1bON7nsmz8N%2BP4kNU0NVqJHneelLL71UEtFMLGbM%2BAAeKqG4ePEiDQYQqdrxOp1hrd1LGwc9Ux0mVTfVi5bEPNOsBc3qZJgLkIA5yvASAZqAFAArAVaUgEh4xR0wl7UFyQM3y7teWUSulJnNuelvbW1FFy9elESkZ2IxY8b356ERiitXrtCFCxf41ta%2B2%2BkP6wftrHXY49VePOclepVLtk6GNWGYD5CFSQnIexlSQ4ABoVAEnQHSeCh0CM0aZFXmeeg3mcq2mFK3ucoLVhQ5OC8BIL58%2BXIJQP9oznzGjIefh0YoAFAcx%2FYozmoH7bS132a1frbgJvoUl3yVDG8A5GIiDhowJQA1eQ4CDANIQBuOEgw6Y5BKoJQC0jhYbfpUq1a54%2FjOoHcDabIPrRlz3ZAARK%2B%2B%2Bmrx%2Bc9%2FfmZWzJjxPXgohOLq1auU27bob7eDwSCr9wa8Okibbmw2uBInyfAaAAuAAUwK0jGgY5DJDUEBAAwsGPLIsADGeFDGQWo4pGKQmsOgArHgYb7hk23bTq9NpNSQiEgrpeRbb72lfv%2F3f1%2FNsiEzZnw3P3KhmAYw%2B%2Fv7Yu%2Bg6x20i2CQVJ1Er3ElVsiw%2BsTVMBKkIzDdBtcHhqm%2B5izXjBSMAZTmJI1PhtdJs3kyfIG0CVEYC8N44qYI7sB1FqleMVRXuR2NboV5rorBYBy7rpt97nOfUz%2Fq6zFjxsPIj1woAOD27du4s7fHuweZdTAMxKhsMMmXybAGwCzAGJCOwdUOPHZb%2B85hyfUoA%2FKCCNpoQCrDtbEsaUI7V0tWaVKu%2BBoZ1FBKC6MY2OkQbEvAWpqDXzlJZTGy4njXbbe7HmN29O1vf7v8whe%2BMMuCzJjxPh4Kofj2t79N3W5GUSZYrCqstBqkRR0gB5OgZQHSQ9jYMi1%2Fv1xqyrHgrB%2BNy1jKUpZSEhdCcCa9oiwqoyyvjAvlpkpwRRYZqqCQhGGksdMhVDwHJ%2BfmyXaa1O9tiiQZiDwvGGPl9%2B0fmTHj%2F888FEKxt7eHwQAkWZ2k5cDYIcBcgDgAgKAgKIXPh7peyYr1ldpovrXQGwy60e7urhoOOZaXQy6E6yW5Ltq9gkzvkJdFlWldI8NcGDAUUmMUE3pjjqW6CyYClCUwGPVpNBrQ5ub1H%2FGVmDHj4eShEIrd3V3AmgezBDS8SXaDBAACESCYgcslfCFN4EIFnpW3WvP5yZNr5dmzZ9Wbb76JCxcuyPF4rAZxDM8d28pIr%2Bj2bKV6kLoKMAvGcJTSICuAQnE4sKANkEQJtE5Qq9V%2B1JdixoyHkodsHgUDGOHu%2BghGgGNxVHwblcAmwcDTNHY6nT17a2tLAKALFy7g0qVL%2BrOf%2Fax89NSpdGV1MVmaC%2FLQyxTHyJDJMC2TmAQ%2BDZQy0MbAGEBDoyx%2FJCc8Y8aPBQ%2BFUKysrMAPLNhCQyADmRQwEoABQOBcwPcrqFbmmWVV7Sgqq7u7nWa73a9sb2%2Fbb775JhER3nrrLVOtVmWtHua%2BxzPBc0kmNjDF0f8LIDKAUZAyQ1mkIDIIfd80m01j2%2FaP8jLMmPHQ8okIxXTgDBF913H58mVaXl7G6dUF02o4xrEyMDOc1EoYCWMMlOEAb8CtnCG%2FsmGBVcMkka3xOG4Oh7HfaDTY5cuX6dKlS8YYY7gRUikplZLaGDkxI2BAZMDIgEyKIm2bNOkYzrVqNOpyfX1JP%2F%2F887N0x4wZ34Mfeozi1Vdfpd%2F%2B7d%2BmX%2Fu1X6OXXnoJe3t7x99bXl4GALz44otsb6%2BDrZ2uFocw3bRnUt2F0j4MVVBKjrjwkZsVtAKXCUfYQ1VQnvdK2y4ix3GS5eVldfXqVSIiUkoK4kIwEoxIAEQgEBgZ2LwE033E403Y%2BlDaNqVhUM9arXrZarVmqdEZM74HPzShuHr1KsVxTMYYobW2RqMRL4oCaZpSURSwbRtRFJnRaISFhQUTBAFz3VALdyyxF5lOvGtS7ZEhASldjFOBXuxirjaHMEwpT%2Fe5kpFbrVa8%2Bfl5C0C5v79vksSIKEqdNC1daVxhKCCQDZABpwI2G4OpHVOk24qLURaEPG42a%2BnKyoo6f%2F48rly58ommSGeVoDN%2BHHjgQnHlyhU6efIkxXEsdnZ2LMuyPK213%2B127SiKmNaToKLWGkmSYDAY6CzLJICy0agwJixpzEDq%2FQPWjQXLlSFtGihyH%2BPYwiiW8FkJrTU4J7Ish1lWwJaWlujGjRtsPB65h72%2B3%2B5lTpy1uKIagSyQycHNGFztGZR3lCUGWRjyaH6%2BGZ85c1ouLy%2FzarVKP3H%2B%2FIO%2BJPcwuuu57%2Ft4%2BeWXTRzHZm1tTc%2Bmb814WHmgQnH16lXa2NhgeZ47%2FX7f73Q6QZZlAec81FrbQghqNBrHw26FEEjT1IxGoyLLstzzPFOp1PhSi6uyiLSWt6ifRijUEhk0UMQWhv3MWPmO0VlPGp1mSRKnUiZlFEWUZZl7cNCptDvjSm9EdiJrpFkVMAqkBuBmB7a1ZTw%2BLGsVipcXW8n6%2BjwqFSfQZYSD3RFpfXfY5kFUdPN7vlIAlAKM0Tg8hCnLVI5Gcf6nf%2Fqn2cWLFxWmUdcZMx4iHphQXLlyhc6fP8%2F39vbcOI6ro9GoURRFyBhzK5WKHQQBdxyHhBAgIpRliSzLkCSJGQ6HqixLGUWRLkvJLMvmjUphsjRSZCKK8i6VZQUm5SbuF8YpRtLh49iy1DDPkzhNUzUYDOxeL60c9sb1bt%2BEcV63pGmRMQQyHTjYNlV3H63KWM81TVmr2lKwmMejvMbUoR3bnDNDZBQApR%2BMRkzhADiDZoBUQJRqJKlBWRhdlGWW5%2BUgjmOdJEk2G6Qz42HkgQgFEeGrX%2F0qvf7663aSJNU4jueIqDY%2FP%2B8tLy%2Fz5eVl1mg04Hkecc5hjEGe54iiCMPh0PR6Pd7pdKxer2dGoxGSJIFWha74heasVJU8V3nR0wYwwmjJQVkYWmPXrQ6DwE2JiG5u7vh7B4PG%2FmFZ7458N5HzXEMQ04dwsGsa3r5ZbuZYmuPGt0sm856XmsTzmBRGMItpzqk0pBMNnWiYEtDG3N%2Ffd8KkNMRigE8oOcMwNdhra3T72qS51kWh47wo86KQ0WAQz0rIZzyUPBChuHz5Mn3zm9%2FknHPPGFNzXbe2srLinz17VjzyyCO0uLiIIAggxOTHaa0hpUSapoiiiIbDIXq9Hu3v72NnZwe7u7um0%2BlAykJxpot6jSWW5WTSUImyzG2b8larklWr1TzLuLmxuekOB2W121PV3a7rDrOmUMYljg4c2jMVa980%2FNiENiOSmpV6YHvW0Kr7BVttGbbQAHOYgUk08lKihIJSBuY%2BHQEiABbBMAbJGEaaoUw00rFCHhsUGdQ41mIwVjxPDNqD%2BEHcjhkzHjj3LRREhM9%2F%2FvMUhqGttQ4rlUpldXXVfeKJJ8T58%2BdpcXERYRhi6nIAgDEGWmsopVAUBbIsw2g0wtzcHBqNBur1Om1tbbHt7W3e7w8ZI2kaNSutVptjLZDKhJXz855mjFEU9dzOYVTbb2e1ztDzRnkoSuOC09D4%2FAAV5wCB3SOBFFlUktAZNasJa1VLzNeBekjkWgShDUqjYIyCIgXFAHO%2F4QJGMIJQGsIoInQSoB8Z2NxgdZFMXhD2D7VJM2WKWWhixkPMfQvFxYsXSUop8jz3hRDVer0ebGxsWI8%2B%2BigtLS3Bdd3j1zI2nW852d4lhIBlWbBt%2B%2Fi54zjwPA%2Bu65IQggO33eFwqEajUSaEGK%2BtrZXOvCOr1Srt7e3ZozitDcdlqz8W1XFatUvjEmcRQqurW2HX1IIxc%2FmIuBkR5zlVfYlmVaMaAowTxjEhywCjAJlpZJlBYQiaAYbu0xOgSbhjNDI4GChEGWDbhErA4ToMRKRcRxSVUJTccbRft83t27fv72fOmPFD4L6Egojwla98hba2toSU0guCwF9ZWbEfeeQRWlhYABFhOBxCKQXf9xEEASzLOhYMYwzKskQURUiSBMYYNBqNYyFRSrE8z0VRFH6WZWVZlnGWZempU6fUO%2B%2B8w7vdKBiO8vpgLKpRFrqFqXBGOXzRlY1gWDYrqamHidWqplYtyMh3FDzXIPAYHAcQnMDYkXfBAeYy2IKBazMt5rwvDICiNJBtjUEM5CWD69lQxkJ7yPU4oiJTVhw2qvFi0CxPnjxnvva1r93fD50x44fAfQnFxYsXiXPOhRAOEflBEDirq6tsdXWVgiBAlmXY29tDp9NBEARYXl7G%2FPw8fN%2BHMQZJkqDdbmNvbw9JkmB%2Bfh7z8%2FNoNps4Ks7CeDxmw%2BFQ9Ho9X2tdybIsvnPnjjw46Fv7hz3%2FoK2DfhS4mapzRkBoD1WzEmfzdSSepYhREvp2wZs18NAXsG0GwSddqUTv0wIOcAawadbBHLkf5uO5IcYA0mhoKJSKoIwPZrXAnIopU2OiIs3zwkTVaj1dWDglV1ZW7ud2zJjxQ%2BNDC8Xd8YUpX%2FziF7G5uSkcx%2FGMMV6z2bQWFxep0WjAcRwURYHRaIS33noLWZZhY2MD58%2Bfx9raGowx2NrawhtvvIE7d%2B4gCAJ4nofV1VVwzlGv1zEajdBqtTA3N8fiOLaTJPGIyDHGFGmauYNhHvbGrhsXdaaNC5f3VS1MspVFa9Sqh1GZKyuLbHHQFXycGKoEDgtDH7Ztv3c%2BdwvB0eM0hiKlRFmUUGrSc2ImF%2BD4%2FN%2F%2F9fTfaHLBYLRBlit0%2BxKDiKNaX0Fr6Rxac0sIupGRd7ZVp9Mu8nxcbm1t6d%2F4jd%2BYBSpmPJR8KKG4evUqvfLKK7S%2Fv4%2BXX37Z7O3t4dKlS2Zvb4%2FiOOaMMcf3fbfRaIhGo0Gu6x7HHIgIBwcHePfdd3Hnzh1kWXbsWnzrW9%2FCH%2F%2FxH6Pb7eLxxx8HANi2DcuyEIYhKpUKarUams0mHRwcsMFgYAFwwjAs81KFubKCXFVtaerMkDQWG8uqW8ZzjfpgebEVjfqwD4qMuiNjemMZLC0t2tWFUyys1cA5hwFgtD5qN38vwFqWJYo0wXDcR6%2FXQRyPoVT5nkjebWEcC8jxl9AGMIaglEFeEPKCYNkVtOY3cObRT2FhcQnb29sYjTMzHsem1%2BvpN9989UHe1xkzHigfKBRXr14lALzb7dpSSjoqt5Zf%2FvKXdaPRoOFwyJRSolariUajQZVKhaYCwRgDESFJEty%2BfRtbW1twXRetVgsA8Nprr%2BG1116DMQbr6%2BsAAM45LMuC67rwfR%2Be5x0%2FMsa4UsaO08KL08JPc%2BGVuiIMeWC6YyyWlraNOKh441otjJUqOOv1yrQQ0verfHHlgnjq2c%2Bw1dVV2JY1iYNoDa0UlFLIiwJxHKHf7%2BHwYBdFoWGPOyhFDk0FYI5mWtxzhd4TCq0nMYk8M4gSICsmcy8Ajjnfw9zcElbXTqLZbGA0iuA4Nji%2Ft3JzxoyHkQ9jUbDxeOwmSVLpdrsiSZKUiKKlpaXi2rVr0FqTEILq9TqCICDf948FArg3YJllGXZ3d9Hv9wEA%2B%2Fv7ODw8hOd5KIri%2BC82YwxCiOPjKCtCRMS0VjzLUitLczfPHavUjMEU4BQbS0jp2nZe8cLcsqxSKVUcHnZVFGVWszlfW1hYNo9sPGpOb2yQ4zjvuRdlgSRJ0Ot1kWcJ8nSIIt5BwHdRW%2BrDZjEEkwDdNVKH3js0AVIDaWbQ7Wts7yuUpYFWBpoRtHFgWwxhGKBarSIIQliWBc4figFjM2Z8ID%2FwN%2FXKlSt05swZStPUHg6H1YODA28wGERaayWEUJxzk6YpgIklME1zTrMaU44yGBOTvihQHo2TKooCUkoopSYxgLv8%2FffPrTgSHzLGcKOMgNGCKOc2hhA8gyeGphIYaXGW57ksl5eX1Ztvvon9%2FX0lhFCMcW3bNjzfRxiGcBwHUkokSYzxKEX7cA%2FbW9exdecNDNvvwlY7WKv1sFJNUXclLGZwnC2lo3GeNoOyCDkIw9TgoGsguEGWGRA0SkkoJTBOCLbNYNuT9O%2FUvSKimUUx48eCD%2FUnLc9z6vf71u7urt%2Fr9TjnvKzX66pWqxV5ntPUgrjbkvhevF8MpkzF4O7Xaa2PYwZHBxERc13X9n2P1UtjSSMp8BOAcoQeTKteV3MNX3leqL%2FxjW%2Fg6tWrqFarptlsHv8cYFIZWhQFxuMR9na3cfv2O9jdehvj3jtAdhvLdhvLTowVN0eLKbi5mWRCCCBGgAUYwaBsYCwYDlKD4digN5hYEtUKg2VxJBnDMBIolAfLciCEBcuyji2lmUjM%2BHHhBwrFpUuXzMsvv2yKopC9Xq%2FodDo0HA4D13W153kQQozn5uZ4mqZsMjBGQWv9XZbBNO4w%2FZBwzkFEsG0bruvCdd3j%2Bop7Mg5libIskSQJ0jQlrbXgnPvC4qZRDx3HlawoQYxxE%2Fi%2BaTRC02pUUK1W2RNPfBFXr16953wmlaA5BoM%2B4jjCzvZt3L75bRzsfBsmu4U5p42VxhgLPEe9LOEnElauwcqJSEAQyCNojyNjQE8ZbKcKt7oGB32DtACIJpZDoRyUsQ1pbAjHg%2BOGEGJyjh8kqDNmPGx8GItCt9vtot%2Fvp3mel0opVylVK8uSyrJ0ms2miqLIAcCmbsVULIwxsCwL9Xodi4uLSNMUrVbruO9jdXUV%2FX4fnudhYWHhuIpTSok8z6fdpRgMBuj3%2B4iiyNLGMCkVDARTmpFSDCCaBCPTnI%2BF5SilnDRN1UsvvSS%2F%2FvWvE2Ps2M042N%2FDaNjBwd4d7Gy%2BhWTwHVT4FlarI6z7OeZNCS%2BVEJECpZMuUnPUs6EFQbocscNxUBBuDgzu9AwGMQBiEJaA1C4KXYXhTQjPgW0AoRkcxz8%2BtzRNkaapUUrNBGPGjwUfKBR7e3sYDocSQOb7fk5Eoeu6juu6dQCelFIaY0SWZWI8HlOWZZMPbZ4jTVMIIbCxsQEAKMsSp0%2BfxurqKmzbxmc%2B8xmsrq5CCIETJ07A8zykaQrGGJIkQZIkGA6HODw8RKfToSiKUJZKjMYlcukhVwEZOPBcgYqneBxH3mA0qvuupcLQ0ysrK9kzzzzDd3Y2RRKP%2Bc72bVQCDc%2FKkAxvQcW3sczbOFlNsOqWqJUKViRBsQbKo2G8LsG4DIXNMBIMXcPQiQj7MbDbN%2BiNAA0Bz%2FchnAaI5uHaK3C8RYzjEocHXcRJDMY4kiTB3t4eiMjs7%2B%2Broiik7%2Ftqfn7%2Bh3mPZ8y4bz5QKC5fvmx%2B%2Fud%2FXgdBUBRFkdq2XVQqFWt%2Bft5xHMdOkkSPRiMwxni326UoipDnOYqiwPb2NobDIVZXV7G%2Bvg7bttFoNLCwsADOOarVKs6ePTvJeGiN0WiEOI7huu5RDGF8bE1Mxue5ZGBhMOYYpDXkZhXcnkOdu%2BQFKVc4cONoH0UWac6hiqKgtbUlKx73vWjcsfa236BQ3KKTiwVWvAGa3ggtnaKhJYKhAs80kE2sCLIABBymypF6DG3JcHtgsDUA%2BrmBBCAVA7gFKavQbAVh8wyac6dRb67BdkJsb%2B8gSQpkeY48z7GzswMAWilVDgaDFEDSarXKtbU18%2Bqrr86KrWY8tHygUBhjcOXKFbO1tVUURRELIZJKpeIsLi46tVqN9ft9XpYlRqMRBoMBjcdjZFmGfr%2BPa9eu4eDgAM1mE6dOnsTiwgIq1Sosa7JP1HNdFFmG0WCAnZ0dtDsd2I6D1dVVeJ6H8XiM8XgMYwzm5uYgLBf9kUE68jGWGyjZBgSa8JgD5pdUCatCJtIr8wOtlFGjUdcusp5dCbKaZ8XOUn3IVqsMZ%2Boa675ETRZwIgmeKlCmATmJRRifQVcYyorAyGbYLwi3ewZbXWCYGGgwMC6gjQvwJsLwFJbWzuORRy5gcWkNQViFLCVGowicc0gpMRqNTBzHut%2Fvl5ZlxUKIQa1WGy4sLOSj0UjPhtXMeJj5UFmPN99806yurso8zxPOeUxEAefcWlpa4idOnKBWq4Xr169Da42pUOR5jm63i7fefBMEYH9rC6dPnUKr2cS0hiGJInQODnD7zh3c2tpCUpY4ceoUqtUqiAhxHCNNU1QqFSwtLaMogX4cIdMtlLQOxRYB4yNXApoRwioR7BEfD2NPyqQx7LUDI%2FfFcqvvLTZL%2B8yaoo0FYNE2qBQSItEgBzBggEswADQjlDZD5DB0DMNOH9jsaRwODbKCwC0LnLmQpgrDF9FonMbaiQvYOHMBa6sn4Pn%2B8Xt3HAdlWZo4jk0URcoYUxhj4nq9PnBdtx8EQfzYY48VN2%2FenKnEjIeaD1vCbb761a8qIUQ%2BGAziNE3T4XDoJEnC19fX0Wq1IITA%2Fv4%2BxuMx0jSF53loNpsgALffeQedd97BTqOBVq02EQqlUEQRhv0%2Bdns9tJVCfX0dtWr1eLVfmqYoy3Liriwuot2JoIxEaSowrApDDrThKBVBagHLDmGxGmWJLbQ8CAQ78JqNDltfzPmJFU3ri4waHuAUGjpjKAQBgQH0pPTaGCBTQCcFNsfA7tCgHwNRBuSlgDI2YKrg9hJqlZNotDawtHIa6yc2sLKyimq1BmMMpJSQUkIIYcqyVHEcF8aYzLbtuFarjefm5oZBEMT1er28efOmBvChpn9funTJTMvfZ8z4JPlQQmGMwdWrV41SqgQQF0UxHgwG7vb2tlhYWOArKyu0srKC0Wh0fCwvL2NjYwM7m5vo376N7Pp1ZNevQwkBcA6uNURZgpUlGOeora%2FjzMYGzp07h4WFBfR6PRRFAaUU6vU6ms0m0tRAiP5k2xfuPo7fKIxRIJTMFin5boKKl8G1JUlJOOwBfcLExVAANMGY9z6fSgPjxGC3q7HT0UgKgmVxMG6DWSEMzcGvnsTS8lksr57B%2FMIKGo0W6vU6qtUabNuGlBIAjiovuTHGSK11HIbhqNFoRK1WK6lUKnmapubmzZs8TdMPtYQpCAL84i%2F%2BovnKV76ir1y5YmZj%2Fmd8knzoGuKLFy%2BaK1euKMdxUtu2h1EUOQcHB%2Fz69ese55xblkW%2B72M8HqPdbmNpaQmrKyt48oknMNrawt7%2BPha3t3Euz7FkDAhAF8C7to10cRHh2bP41PPP47HHHoNl2xgOh8c1FUQEwQUc10LoGbh8hLjsQ2sfjBkIJsBRosgHKLI2jO4j8BNy7RxlqXDYVeiPJntMCXhPW%2B7WGABSGgwjg3ZPI84YbNtGEIbwnHlUKifRaJ3B0sqjWFk9iVZr0i5v2w5c14XjOJjOAz2e3lUWAADXdeB5PizH4lEauXE8spVSRv%2Bg1KhSk%2Fm%2BCtCkjZRSK1LlQnMhD4KguHLlip6JxYxPio%2FUbHDp0iV95cqVMgzDSEpppWnKb926RQDcVqvFjTE0Ho9xeHiINE2xtLSEM2fPor%2B%2FD7mzAzEcoprnWJcSBkBsWZDVKqqPPorHPvMZXHjySSwtLSFOkuPCJGMMRqMRDg8PkUSJca3chE6EcWKRNiU41WBpGzpXNOofgMk7CKwOWtUh6mE%2BWSEIOrJC3sdxM%2BhkMIUyBKk1klwgzV1wZw5OeApLq49h%2FeQ5rKxtoNVaQBiGsCz7%2BD06jjMJ0B4hpZxka3o90kYJr%2BL6xDRPyqjyl5qv%2FVWfkj%2FfTdzf%2Bcbw2b%2F%2Fva7zpORdQxYKZT5pcbdtR0olU4usgWVZY9d1ZxO7Z3xifOSupPPnz%2BubN29mSqlhkiTU7%2FehtaZer%2BeWZcmiKKJGo4EkSWBZFuYXFrBx7hwO79xB5%2FAQB0mCufEYkgg7YYj05EksPfssHnvmGaysr8MPAuRFcdxByhjD%2Fv4%2But2uFsKSDIWs%2B6mJoltcp10GXSWkFrJBScOyR4HbZjU3JUY5YEoIbiDEUZn4XedhcDSVXwLGTLIYgjiEDXBLwOELWFy9gHPnP4VHzpzD8soaarU6bNu5p%2BGNMXZcVTpdQdDr9XBn8w6297dI8ZwHTcstUTjnxdbptUr2C0VJnCN%2B9N9gf%2FxTf1Sc%2FW9HrDqevjutDXSpkCcF4jRFOiqgtTGO4yilytRxHZqbm5PLy8vylVdeKTHbAzLjE%2BAjC8WXv%2Fxlc%2FnyZcU5TznnUEpRv9%2FnvV6PsiyzjTG82WzStFnMDwKsnjiBjaeeQrS7i61%2BH6oooBjD3tISKk8%2FjbPPP48TGxuoVCrHnaOO4yAMQ3iehzt37pgoispKpRI3Gs24VWdqOOqJPG3zorBJJZwKh4SytaPt0k0y4tv7INdSaFQ06lXAsSd9GoSjyVNq0go%2BGDGUykNYacEPZYk5YQAAIABJREFUaoDF4FU55isn8eTTL%2BCppz%2BFlZUVBEEAzsVxxem0%2BnRajp3nOfr9PnZ2d7C1tYl3b15De7QHCgvyfcYVY1jKozMOZ%2FzxL%2FwCxv0D%2FKvf%2F4ef%2Binz9te2qmu%2FvN967C2jgSItEQ9TZEpCsQJK5CAiKA6Ry5S0VgVjLAaQhmE4Xfk%2BY8YPlY8sFMYYEJG5fPmyHAwGqeM4LMsy3uv1TKfTCRljzmg0soqiIK01hBBoNJs4%2FdhjaO%2Fs4K2dHXTiGMyyUHnsMZx67jmcOXcOzVYLtm1j%2Bt94nodarYZGowHXdU2%2F35dpmibNJgaNWpDV%2Fb4ZDvusVIANl9XCmuv7Vr0sc9bJtGsUqO4D1QCohgyVkMCPwoZaA3EGRAnDOPWh2RrmahewvHoSpTQIezFqjUU8evYcTpw4iWq1etwRO21Wu7thbVo3cvPWTVx7521s722in7SRsiG0l0HZEppKSKmVZRGEcHDuJ%2F89LJ56Bt%2F4B5caq%2F07v1wvx%2F%2Fg9snP%2FqM8y02eFZBlCeEwVN1JulXlxjADmMkqM0az2u8ZnyAfayDC3WIBIDk8PMR4PC6llAVjrF6WJSvLkmutiYjgeR6WVlZw%2BsIF7Gxt4Xqew%2FN9nH3uOTz65JNYXF6G67rHXaS2bSMMQ9TrdczNzWFpaYnSNOVFUVhRFPGFhQX59NNnskajIW%2FcaJu1tZCdOrUajIddZ3e%2FH6ZJYjhJBDYDYwKMT%2F7qEwMAgtGA0hzgIWqtE2guPIPzFz6N1dV1RHGCra1tOI6HRqN5PBB4mpacfj6nAtHr93BwcICd3W3c2bqFzb1b6CdtSCuBclIUMkFZZFBaITOFtHwGLTMAGo3F0%2Fi3%2F%2FrL9Af%2Fx%2F%2BAW9%2F5k7965u3fffJfmlO%2FGo%2BsoTEGjjcZ2SdzbaRWCsQyECJpZJrnuVpYWJhZEzM%2BET725JSpWHzta18rB4NBDEBKKbUQQiil7LIsmdaagMmsimqlgpOPPILOpz8N43kIfB%2Fnnn8e6ydPIgzD445SxibZhkqlgmaziaWlJYxGIxRFYR0cHIR5nqs4jun06dPjRx99NH7hhaQAaqosO%2FJaNFBlKY2UBpbnwzAHvbGG2gIcm8DYZHWXNgzS%2BLDcNTxy9lmcffwz2Ng4iyCs4PDwEIPB%2BNgFmgYs70YphfF4jJ3dHVy%2F%2Fi5u3LqOdv8AiRwhwQC520emI%2BR5glLnkEdj9ApHlwCgZAEYIG%2B%2FDea38IWL%2FwWdeP338Pu%2F9beffYFu%2FK13ndZ%2F%2F6ZaeZ0ZDl0QirQwyFG6FX%2Fs2s6QNMW1Wq38%2Bte%2Fjueee%2B4%2Bbv%2BMGR%2BO%2BxqxNBWLixcvys3NTYzH47RSqeRaayWlxHRzOWMMtuNgYWEBTz%2F7LOaXluDYNtZPnECtXr9nORARQQiBIAjQarWQpimO3BhGRE673a5nWeYcHBx4URQNGGPjeh0lY4w5joNarYaFhTnMzzdQDS0w5ChMCaUJnDg4F%2BDCQuBVsbR8Cmcfewobj5xFq9mCPup2nc6JeP9A4Wnn5zRY%2BZ1rb%2BP6rXdw0N9FhjHglihYhNgMkZYxpCqgjT4%2BJwNWAhpa5jDQ0OUYstMGd1s49fjzWFj%2Fn%2FBP%2Fu7fqD1m2r%2B0wNL%2F%2FQ322X9ohTUdicSMh2NZqzby%2Bfm5JAiCwnXdWXp0xifGfc9im4rFhQsXjOM4xrZtY1mWmVoIUxhjCIIA6ydOYG5%2BHowx%2BL4Px3G%2Bq9WaMQbXdVGv16HUZFuwEII459y2bbfb7Vq9Xs%2Fu9%2FuWEMKO4zheWFigSqXCGWO0vr6Oxx9%2FDGHoI4knO0MAMxkcY1twHRd%2BEKLVmsPS0goajSZsx0ZRlHef0%2FHzaTfsYDDA%2Fv4%2B7mzexo1b13F7%2BwZ68SEYa7vrxZ0L9WT4dIXKZwUn823j%2FO0%2FE97bQgjYlg3H8cA1SSAGtAQMQCBAFSijHah8ANufx8Wv%2FCr9s3%2Fydwze%2FubP%2Fmv8m0%2BoUxd%2FZTdeO9za3DKe5%2BkwrOsgCPTFixdnIjHjE%2BOBDW0MwxBhGLIgCITv%2B9y2bdwtFtMBNr7v3xOPeL9Zf%2FdrPc9Dq9U6rlVwHIeCIKDNzU3a39%2F3hsMhH4%2FHdpIkjuM4hWVZolar0draGs6dO4dms3W0CHmAspTgnN01LMdDEARH2QwOY3A8rk8pBc45tNbI8xxxHOPw8BA3b93AjVs3sLd7k%2FPOH5xdzneePq2jZywtHwcMt4SFhbXHTDrYoqfK4kvfcby3XceFa3sQ5EBGeQnE0Hqa1ZwsFyFjoLIhVJmCJR385J%2F%2FK3Ty7Kfw6m%2F9%2BhPi9m%2F%2BzbXm87%2FSCx75PcbYcdftjBmfJA9EKI7WCrIoioTrupbnedxxHHq%2FCPwgcXg%2FU3N9OqzXdV0EQYBarYZWq0W3bt3i77zzjrOzs0NRFJnxeBz7vs8450ci46PRaCAIAjiOgzRNobU%2Bnu3pOM6xyzPNYEwH5uR5DsuyMB6PIaVEr92m7%2FzR3z05vPPNpyk7fHrJZE%2BS0S4RQ6W1ajae%2BRlaP%2F8i5taegO1V6eu%2F%2FDnkcT%2BsVxuwhQuUHPlQIk50iRpgtDya6P3eEE4igpYpZJlCFREWWjX85b%2F2S%2Fid%2F%2B1XQt3%2B55fPs7fP3eJ%2F7r95EPdrxoyPygMRihdffJFef%2F11IaV0LMuyfN9nnufdY1G8fzzeh%2BFuy8K2bXied7zrw7Is6vf7rNfrMaUUAVB5nsMYo%2BI4NtP9IUEQHA%2FCKcvyuPZhGoeY1kQA703WStMUg5t%2FsND55mvPlIPbT5t8%2BLTRZc0G4FWa5sS5n6H1Jz6HxVPPImwsE%2BMWcFwwJcEBCMaZywIUY4N0kCMZ5RhbsgAAo9Sk%2BOEua2uy55QAI6HyMajMQayLv%2FhX%2Fjp961%2F8P%2BbGG%2F%2F8Z8%2Fmv3VO7b7281j7z9%2B633s2Y8ZH4b6F4uWXX6bxeGxxzn3XdYMgCJxWq8XCMKT39z5orY8%2FpB92buRULO5%2BfZqm04YrZVlW4ft%2B4nlePB6PRVEU4eHhodnd3TWtVovq9frxZrC7J35Pmbob7dt%2FUn3z9%2F7OUwc3%2FuUz%2BfjgGah8EQCE7WP5sc%2Bak%2Bc%2Fh8Uzz6O%2BcJqE5QLEYIyCLlOofAStJIRbBUiAMw1OxKLDEnE%2FQ56WgCGUllUCgFbl8Y6Qo5PE1KowRCBMrAsDgipu4sknn6Sl9TP4o9%2F5%2B0%2Bz5PYfJX%2F4H%2F%2BHeOmlf3S%2F927GjA%2FLxxaKK1eu0Pnz52lvb09sb2%2F7cRzXhBDVpaUlZ319ndWOtnEppZCmKfr9PrIsg%2Bu6qFar8DzvnoXFHzS9ezo5ezAYYHt729y%2BfVv1er1Maz3inA9834%2BklL4xRnY6HfX2229zzjlWVlbo%2FQHTu4Wic%2F2ftV7%2FP%2F%2FL%2F6pMB2cAEOMW5pbPmPVzP4mVR57H3OpjsLwaEWOA0TA6h0zjI%2FcBALH31gpgYlEIBjBVnP3C6E%2F%2FF3AA4eR7jBEDAhgjj376va7HVCSOrQstoY0GkgINS%2BMv%2FLv%2FEf7gd%2F9xEI%2F6V%2F%2FHn%2BNX%2FpPfVJc%2F7v2bMeOj8LGE4tVXX6UXXniB3bx50zo8PPTa7XZNSllfWFgI1tfXrfX1dQqCAEopRFGE3d1dvPvuu%2Bh2u6jVajh58iQWFxdRrVbhuu49FsP7BWM6kTtJEnQ6Hdy8edO8%2Ffbb%2Btq1a3mv1xsbY%2Fqc86her8tarYZ2u60Gg4G8du0aH4%2FHfH5%2BnqY9I1P3RQhhABhjDPb%2B6aV%2FX%2BXRo49%2F%2Bt%2FE0sanYNdPw6%2B2jtwJYDSOoYdDaDX5cBPjIGJHjwQifvz%2BPQoAGFiWgOtW7XrLW2UEsKNiLzIawAhG6e%2FaWYq7RGJ6HQwRSBtoLUHGgCV7%2BOnPfZ79qz97HbtbN%2F7rv%2Fll%2Bp%2B%2F8oppf5x7OGPGR%2BEjC8XVq1fpxIkTvNvtuoeHh2Gv16sMh8Oq4ziVMAztRqNBQghEUYR%2Bv492u43vfOc75s033zSHh4emUqng1KlTdPr0aVpZWaF6vQ7P846Di9O4xt1pyWnr%2BtbWFm7duoXNzU3ZbrdTrXXseV6plBIHBwdOpVLxAYiiKEy%2F39e9Xo95nmcmW8YYXNdBo9EwlUpF2bYtKXq3YsXtn1p99Dn8xF%2F4a3j3ziFu3dyD1ttQSgIgcCEmtRdcgPHJ%2B2NcgHEOzjgY53AcG7ZlowkPtsXhh01U1x%2FBk4%2F%2FJTi2BdsWsC0BBok3XvkFeMFEUHBXUPdukTDTbAgIhhiINLRWMNBAEeHcufPY3brBpOB%2FEcDfe2C%2FDTNmfB8%2BslBsbGwgyzIRx7Hf6%2FXqnU6nHkWR7ziOzRjjw%2BEQN27cMIwxjMdj7O3t6c3NTbm%2Fv1%2FGcax6vR4bDodid3fXWlhYYM1mkyqVCgVBcLzfYxpPyPMcURSh2%2B2i3W5jMBggiiITRZGSUmqttYjjuHK0Nd2uVqs%2BY8w1xliWZXHGGFNK0WTqlMJoNES32zNB4KtarZaujf%2FvnzZGi0effBFGFrjx7jVIZcCFBc6tiUgQgTMCZwyMEThnYHxSrck4g%2BACnHFwzkDEJjUScgxTJijTAbgS4MqCVhaIGYAI2WAP0BIiXIGRKXSZAMC9IkHfQziOnttUgBgDtPoSZkIx4xPgY7kew%2BHQHBwcmE6nw6Io4lmWUZZlutfr6WvXrtH169eRZZlJksQkSVKWZZkyxuJms1lorXmSJN7m5mawu7vr2LYtbNsm27anYkEAaLrTI01TJElyNIV7soPUGCMA%2BFJK58iloCNhEJ7vi2azRZ7nket6k%2FgEY0jTFLs7O7h16yZtbt6B53nlydX9F7mwzPKJx2g07KMsS3BhgxGBcToKvLIjcZgEVadWBGNHVZ78veAsYwRjJIyMoVWBMu6CSwtcWRDaAnGGxtqz6G99C9%2F5p7%2BE9U%2F%2FHPz5J1AM7kCnHRit7hGEux%2Fvfq6VxMYjj%2BPGu2%2F%2FzJX%2FgNxLf89kD%2FbXYsaMe%2FnIQvHcc8%2Fh13%2F919XBwUE2Ho%2BHR0t%2F3OFwyHZ2dtButyGlnC4d1o7jFLVaLWk2m9Hi4qI0xohut%2Bt3u91iNBr5eZ47eZ4LYwxzHIdXq1XOGGNJktBRuvO4psLzfQS%2BT7ZtW47jcCGEcV2PXNeF53nkBwEFfkC%2BH8DzffieD9txwbhAEidgzMbNW7ewt7dPdSdxxar6yaWTT5CwLRzu7k3iCMwcicTkIIbjfg%2FGCWwqHEcWxcQNYRPxIDYZcKEng3mMLmE03juIYeHcF2GHLXTe%2FUO887v%2FHeqnPoO1p%2F4dcLcGObgFUyYwR1O9zF1BzknAk8HQ5Hsrq6u48e5bXi3jfw7A%2F%2FXAfzNmzLiLjywUR12jqiiKVGutjTFpWZaWUoqUUrAsC1prMsbQUfBQMcbKoihKzjlzXdc6yl6UZVnmRxvBdFEUvCxLc%2FSaSXDwKHYxPVzXhe8HCIKAgiDgvu9jKgquO3FdbMeFbTuwLRuWPdn3CeIYDgcI7myCMQ4pJb14dvyCgbFPPPIUoCX2DrtgIHAiMCJwAjgDOCMwAsSRePCjYyoYnE%2FcEcEnFoVWJYzRIC1hZArDJDQroZkFrSYxidrSOYTNU%2Bjc%2FCMM7vy%2FGG19C0tPfgmt05%2BFGWzCJIcw5v0icW9WJHCm8Q3zJcyEYsYPmY89j%2BLixYsqiqLUtu1cSklaa9RqNVpYWJiKgVWWpSAiPhwO3U6n48VxbDebzUBr7eZ5Lhhj7Mj14JzzqUVBlUoVnufC8%2Fzjkm%2FHORIN18ORBTH5t6Pv2bYH23EghH3kCkxcBAIhLwp0e30kSQatDSzbxnJd%2FTSIsHryLMosQRSlYMKaiAQzqPIBOHfAhQ3ObTBugwsLTNhHQU0LjFtgXEEwDU4KHAWMjCdDfvMeZP8d5BaH4hylxcDfl9HxaosQtofhwTvY%2FbN%2FjIPvfAMrT%2F9lVBqPoOjdgNIKPyh1urS0ag72d78Eol%2BYjeae8cPkY8%2BjAGBoMojyuBb5i1%2F8Irmu60ZR5JVlGZZl6TLGbGOMMMaw0WgkjDGO67rC931WqVTIsiw4jkue55HneQiCkIIwIM%2BbuA6OOxEI23Zh2e5RnMKGZdkQlgUhJgfn7wkEsaMPEwhKSYyjGJ32ITqdQwAazYrPQ4debC2uwvN87Hf7IBgwmiQiGGNQvAJwG2D25JFbIG6DuAUzPYQAuAVYFogLMMuDpqO6EBGAe3OwLH6U9ZgEPN%2BPW11EZfEMxoc30d9%2BHVv%2F4n%2BFt3AeK0%2F8DETRRxm17xGJu1Onp06fov39naVf%2B1k8958Cf%2FKxfgNmzPgQ3Heb%2BfQpEeH27dvctm27KIq6lLJhjHF937dc12W2bZPrehRWQhYGIYVhiCAI4Pk%2Bea4Hz%2FPhupOYguO6sKxJylFYk8G1wrJhWc4kG8GPAorEQcf1F%2B9Vbk4eJinWolAYDPrY2d7CcNCF53l44XT0BEFXTp46C9ISh53hRCCmNQ%2FEoEQdsCxAWCBhgYQ9eeQCJCxA2OC2N3m%2FR7EQLgQMJqXhzG3Aqp2A41hwbAHXsWAJ%2Fn2vZTh3BvWTL2D%2FnVcR730bt%2F94H9VH%2FnW05jcgB7egtPqu1GmtEkzOF%2BxLmAnFjB8iD6x7FADdvn2bOOcWEfmO4wSVSsURQjDf91GtVlGvN2h%2BfgH1RhOVShW%2BH8BxXNiOc%2BQ%2BTOIL%2FH3uA2PvZRuI7i7MomNR%2BF4opRDHMQ72d7G3twWtJFqtJlbz0U8CwNrJR2GMQneUgJg4DmAyRiBdgLQBaYAMgdOkFsJxA1iOB8v2wIQFGI20exv93jXknWuQnUkbhjYGOulAKQElJwcTP7gZzhQS1eWnIIJljHdew%2BD676G%2Fu4T5k8%2Bh4hcoosN7C7O0RBhUTJzE%2FxaAv%2FEA7%2BWMGffwIIUCvV4PjDHuuq7gnAvOOdNaU5qmIGJwPR%2FEBCrVBubmllCt1uC4HizLPhoYYx8VNrG7hOBeQfhBwnA3RmuUZYFet43trTsYDvoIwgCVSgXBbvZCENRMrblAwyiHVICwpm7LJO0pLAHL9eE4Piw3gGVPrJkijxAdvIW88y7U6Dp4vAnkXZij3g3DbOjgNLg7D6MLGKUnTWBKwdAHCIVSMFqC2QHs5Z9APtqGivax%2BfYfwqqdwNrqOkSxd491cebsWfqz1%2F70qV%2F9OTr5n%2F2muXOft3DGjO%2FJAxWK0WiESqUCy7LQaDRoeXkZnudBSomyLNHpdEFkgcgCFx5cr4IgdOC4PizLBr8rvvBhBeF7YYyBVArR%2F9femUdJltV1%2FnPfEntERkRm5L7U1lU0DfbC1mw9MpA6CCo6Js4cR9QGqpoeRT3qoCJ094jogAe0W6CrwWlGxeN0DnOG0TMKyYwCHqSBXqC32pesrNyXWDK2t9w7f9wXkZFrZWVXdQGV3zpR8d6N%2B27c9yLf9%2F3uby2VmJycYGryAkIourt6SFWf2CeU2zm07xBgsFAoaf8I08YORwmF49iRKJYdBgWV4iz1iWnc0gSydAHbncOUZWzhYiApyxh1owtppTHCaaxwnEho88uqVDNodPX%2B2lMVQLQbV2SQ5RnKS5M8Ob9IR88e%2BtJhRH0JIQS5jnYALIwfB%2F5sRxdsF7u4BK4oUaSCSuWJRILu7m72799PJpPB8zwKhSL5QoFCfpETtRr5QoGlxSV6%2BwbIdXaRTmeIRGJYwnpeJAGaKGq1KrOz04yPn6VUypPJZOgf2EPtsb%2B%2B3QcG9h0CIZgvOIElw8R16tQqRdxqEbc8j3CWMGQFizohoZPNVKRFnU5qKkZNauVlNGQTMSyiwsLc0Paggpz6CkOIVfn1DdGYc3Pyq%2BuPCBPH7sSTVZRfZHx8nDMXYrxoMEVntITpu9iWrVzP%2BQl2iWIXVwlXlCiAZmKYeDxOR0cHPT19WLZNrVpjcVG7Yi8tLXFx4izzc7NcuHCOwcG9DAzuobOrh1QyRTgSwTR3Thie51Es5BkfP8vU5AVMw6Cnt4%2Be3n7O%2FvPk7eFwRHXk%2BoRTqzI5NYnv1nFdB6UkJh4WLrZw0GVKbSoihqtC%2BCrIpcmKqUcggkRVBhgGhhkoP4N3ww5hhCzMkI1hN45fe04KocDAxVAuQrkIz0WYrq5FYoA0I9SEia%2BqOG6VJ0%2B5DN9%2BCNud49CNN4pnnv7uDz%2FwH0TqV%2F5KFXf%2B6%2B1iFxvjihMFrOSQsO0Q8USSZLINw7DIdfbQ2TnP9Mw0M9NTLOULTEycJZ9fYGZ2ioGBPfT3D9KR6yKRSOrlSOAevV3ocPQa8%2FOzTFw4x3KpQGdnJ319g1iV01nfWd43dMOL8WpLLFfq7G1XoEIII6LvfAUYhjaFaldNwGwSgTBsMEwwTAxMlGEG1pmQNtU2zLaB6da2Q1imhWXbmKYRSA5Kh6w3XTZ9lPSxPRfh1gk5LhHHwfM0gbmui%2Bc0th2UV0f6EtcXZLpvpMs1efqp79o%2B1iuBL1%2BN33QX1zeuClGA0Ux5p8mijVgshVKKdKaDTDZHLtfJ3OwMs3Oz5PMFzp4%2BxtzsFFOTE%2FQPDNHd3Ud7Ry6oEh5elS1rM%2BjgL5dCPs%2FFiXFmZ6ewbYve3j66unpZPv3tNEBPbz%2FSrRO1FQf7E0F8hYkwLRA2hmmCYQMWwjIRwgbT0uRh2Ppl2tCyLQxzZd%2BwwLAQwYvG2BiArjCG1OSA9JDSQ0mXkHSxPTdoc1C%2BB76jtz0H6dfB95B%2BHeXXQSo8CfFEqnH%2BfVfn99zF9Y6rRBQBhMAwTEKhCNF4EsuyicVTJJJp0pkOOnJd5OZmmJmeYnZ2hkKxyMkTzzA5eYHu7j6G9uyjv19XDo%2B3FAbejDCklFQqFSanJjh%2F7jS1apme7h56%2BwZIZzqoWTrHRMi2tdYgKA8olEIKCb6LEBWkEs1UdUIRKBKChYZhBtrH4F1o709laOlDYATthj5GBPvK2HAVpaWK1RKGUlKTiJKggrIHyteSh%2B8HfTwQFrbK4tWLjcu9Wz1sF1cFV5coIHhaG8FSJIxtay%2FLSDROPJEklcqQzXbQkcsxPTXJzMwMxdISZ8olFhfnmJmeon9giL6%2BATLZdqLRWOCJuVrCUEriOHUWFuY4d%2FY0U1MT2LZFd08fuVw3sXhCLx9ALy8CkkApJAqkWmeRECh8QEgR3OyBfiGoOKaJAWSDGIL4DBqOUc0s2wSu1wpU8C5AyYYGU%2BqPlNQEJmXQ10dJpWehJMiARJSvtw0Lw0uCriu0i11cNVx9omhCE4Z2aNLRliE7TDSaIJlM05bOks3m6OicZm52loX5OQr5eQr5RaamLjDZP0R%2F%2FxCdnd1ksx3EE8lVqfQa%2BSbOnz%2FDqRPPMT8%2FS39%2FP23pDPF4CtsOtZDACkm0Shag9I2p5Ko%2BND09QQiFaEoczf8aw2oe0HGfemyhjzMaSs9G1xUzRyBR6LkoJUHquUhkQGB%2BUBRZ6iWLkkhfYpg2WHHc%2Bm6U%2BS6uLq44UegsVcYWSwQRKDsFpmFi2SFC4SiRaJxEMk0m20Fnbp65uWmmJi8yMzPD1OQ4kxcvcPLEMYaG9rFv%2Fw309w9pk2o02syyPT11kZPHn%2BPcudMslwq0Z9upVKrUHSdIqqtnoIJ5KBqi%2FmrSWEckcoVIpFrfRyqF9CWO5%2BO4Pq6vcF0fJTTxCCWwbYjYAtvUPiJGMBMhFFKuIQqlaQKplyQyUH6unaOwohixGr6%2FK1Hs4uriihNFKBRqJJjRZLFJv4aIbqIlDMu0CIUjRKMJEok2Um0ZUqk0kUiU6vHnOHfuPJOTEyzMz7KwMMvs7DR9fYPkcl2EwiHyS4ucPPEcFy6cxXVq2CGb5eUS58%2BfJdWWxbJCeJ7XOoHgYb6xZLFKythC%2BvB9SbnmMH12hvL0ArJSBddFSU1APgauYeGFIphtceJdbaTjFpm4RSSkQ9i17LHxd66VOFr7CKVAmKvXSz%2BI%2BGXRjsvPAyB4lAfVv1zjGV13uOIOV7q4jo7wNI1LWypaJQxhmFimrfNJhKMoJVhYWNDmRdMAE3y%2FzvTkeQr5RSYunCOX6yYSiVAoLDExcR7XqTIw0E8oHKZWqzEzfRHDMHFdl3CpEHwjgbKRjQlgA6lhoz6eL5mbWmL60aeo%2BjATSkG8nXB7GMsAWakiCyXilRLZahEKUD5v8pSdJDrUyWB3jFzKIhoyAserLUhrozk2z%2BMHnCgcuhF8HADFHwBXnShGh8X%2FBgZ3cOj7R8bUD1x%2BkKvicKVf26vb0USQMEYIXUzHtBzqjsdSvki1ViOTzdLZ2Uk6ncZxHAqFItNT48xMTyCEgfQ9DFPQ29tNX%2B8A0VicxcUFLk5cYGpynFqtxoA9CbCiaGxIFBsSQkOhuLFk4XsrJHEh2oGfTNGTDZNN2rpyugAlU9TdDpZKLhcm85gzs%2FRQYa%2BXp3yyyBPn2mjfm%2BNAb5T2pEXIFGjN53YlGwIl6hX%2BEXcBcCNwYAfHZa70RL4XcNUcri6LJFqgQ8MdlhYXGR8%2Fz%2BTFCVAwODDEgRtuIJNpx3HqzM%2FPMT8%2Fx3KphOd7hOw4mWyWvv5BensGiEZjFIp5otEop0%2BfYn5uCtuYpLPxPSJQNrJeR7HuxlRKVyVXgNDLjfxSmZnHjzHduYdsW4T%2BXJRs0sYrV1maL2JlU7TFLCIhm55MiKFcmLlimonxAsbkFF1GjZv8JS48u8zXZru49WCK%2FlyYiNXw%2BmqVbDaZowjMND%2FoS49dXHO8gFaPS0NKSa1WZW5uhpMnj3Hq5HNUKiU6Ozs5cPBF7N93kFRbBt93yXX2UiwuUSkv43kelmWTSqXItneRznQQCkVIpfW7ZdmcOnWC%2BnQl%2BCaFwNC6QrX1U1tKRd31MJcW8NIZQpagVveZfPwYi7k%2BOrNxBjujpGIWAsX587NUjp%2FlZNc%2Bbr2xnb72MCFLYCUs4hGDzpTNzECC6ROz5BZnGAi5FCdn%2BGdX8sZb0vRmbUzBpXUmwbvA2OWJXVx1fM8QhVJKh4UvLnDq1DGee%2Fa7LC7MkkmnOXDDQfbtO0hXTz%2FRWAKlFG3pDuo76P5%2FAAAb7UlEQVS1Ko5Tx%2Fe9oOJ5hEgsQSQSwzQswpFYy1LIZLL8bSgATR8HLkkSjucjzp2lvlylYidIxiymT0xQCsVJpGMM5KK0xS1MoXUWJGIAzE%2FmmetN0JW2CFnaWGqbimTMIBqKkI33MP6UJDY7R1%2FY4%2FGZGkvLLl0pC2EFCs5L6kxAlza8Rj%2FaDza%2BCpxu2Q8DP7ymz0Xg6TVtk1dxTtcM3xNEoUnCpVDIc%2F7caY4fe5qZmYskE3H27NvPnr030NnVSyKRxrZDIAThcIxYLIUvvWbWap3oxmr6VpiYRKJxMpkcfX1VqmezqELwpcHafisdhZSS%2FGKJl82c4n8yRGfdxcCndH6SWv9e9rWFSMUMDBE83QXE25PkTZuE51JYdqk7Esswmje%2BQGGZkI6ZGC%2Ft5eSzFo9O%2BexPWzphrlABf11asUrDT%2BMHXZl5DTAypt7Zuj86LPqAiTXd%2FmFkTL3rhZvVtcM1J4oGSRQLec6fO8PxY08zPTVBNBJmz5697Nt3A13d%2FSSSaexQuCXDlfb2tJSNCqSCRo1S13XwXAfHqVOtVigW8pSWS0gpV26pwFNyM6WlkopK3SNz%2FgQRPC44IbqA2dOTFEJxUjGLdNLCNMQqM2YiYiBzWXIzRUpVj5rrEw0LDFaPrQTEIgZ7D3WQ6tWRqtmEGZAOG%2BhM5CaWGaHNpJeLI%2BKlKG4HbgNuQ3AQmEPfDF%2FC4G%2F4lDrX7H9Y3IlgEChwVH181ViHxZsQvC7Y%2BzhHm3S8nXm8C%2BhHscRD6k%2BD8V6B4C3NPga5psJWcAdHxL0tIzgcVR%2Fe8jvuEq8GXo7iVuBWoA8tCTyJ4gkkX%2BUzVz%2Fpz%2BiwiKCv9yuC1w8B54DH0KkMvzUypmYvMcbLgmNb8RcjY6qySX8DOLym%2BbsjY%2BrrLX3eAgy0fP4t4HHgncAdwK3XlCgaZQOXl0uMj5%2FluWe%2Fy4XxM9i2wZ6hPezff5Ce3kFSbVlCoUjTBVsFTlJSyqBSuofnujhOjWq1SrVaZrlUpFQqUCzmVX5pkaWlBZid8bUyUwArkZwbPbV9JSkUq9xem%2BeiY2GGLUBRuzBNsa2H3phJxDZWLREECtsSZPf3IOYWWXQl1bpPOmbgB%2FN2PUl5qUJ5aZl6qYZfriPrDrV4glpPmv4Om0TYQIj15LWhZNE4ne3iXSKLyZ8AP7%2FBcWngBuANSD7MXeJ%2BYvwOH1NVBO8EXgNcAD6%2B5rg3Ae8DwOdhggXedmcEvArBOeBPg%2FN5JXBPs0crDypeD7y%2BpaUMbEwU7xb9GDwILaSzgjcAb0CH7dQ5LO4jz0d4RPmXMfdtY3RYDAOfYb3J9aXAjwfb7uiw%2BCPgQyNjytlkqLcA961p%2BwKwIVGghYFPrWm7H%2Fh6y%2F57gR9p2b8HeEfQDqCuKVFI6VOtlJmamuD4sWc4d%2B4UKJ%2F%2BviH2BSSRTGaw7RCNyFDp%2B3i%2B15QY6vUa1WqF8nJJk0J%2BkWKhIPP5JUqlgqrXqsrzXOW6jt%2FuLFVpuB0EGsC1yszGtudJrJlposLnxHKYtqxFveoga3WMzhCxSMvTv%2BlRqT0tM202%2BUyKSL3CciVKUbgsTy1RnS2iiuUglkPDCF6lgse3puHNt7WxtyuEbcL65dAaxWZDktiuQHFEvA2TTwHdLa0TKB5DcArBIRS3Ab1oNcmvUuaN3C3ueB4%2F805QZzXZGECy5bNWn%2FXyuqOFEBzmMAYfAVJBqwJOIXgCxUX0DXobkAXCCD5Mhp%2FmPeKX%2BJRaq3fYMUaHRRL4KHBkG91t4APAT40OiztHxtS3rtQ8LhOH0VJXE9eMKHzfp1KpMDU1yYnjz3L2zHE8p0Z%2Ffx979x%2Bgp3eAeCKFAqrVCq7jrJBCeZlKucTyclGVl0uUSgVVKpVUoVCQhcKiVyqVvGql6rtu3bdty89ksjKdbnPTFZWgTlMJCGxqDq3UXJLFRQDOOTbxsEFtsUBFaI%2FKiL2y5PCVQvl%2B04piG4JQT5ae58apPpVn2nNwQmHmXYOCF6XqQt1T1H39qrmKt%2BXqzM05zORderO65umlPDZVoEtRyI0u8WocESPAIy0tX0JyF59WZ9f1PSwOIngI%2BFfAS5B8AYjs9Le%2BbBxVn0E%2FfRvzuQnRVBr%2BMUfV7215%2FGF%2BhYZ0ovEZfN7HZ9Tiur7vFq%2FH4GFgP%2FByJF%2FhPeJGPrX1EmA7GB0WFjo%2FyCsv89CXAP8yOixeNzKmvvF857EDrEtX8IITRWPJUK1WmJme5Pixpzlx%2FGkq5SK5XI7%2BgUHa23OYpsXycgnHWaBSXqZUKlAo5FWxkKdYLFAo5GW5vCyrtYqsV6u%2B73uelNKt1ar1SqVS8zzpAK5hRN1YLCp7enq8xPhykjqrgrnUBjEUUknqjg%2BuDzbMyCgHQwbOXI2abwQxG%2FpcPF9Sq7lkpiYo1iWFsstiwcGvO5iey4lKhFA8Qc6EiAlG1MKxonRFDUKWJhtDSQ6VL%2FLRzir%2FdS7KQIdFb8ZCJ%2B1ukINcNcdmm%2FKDILItcKfIYfOJYM9B8Cs8qB7atP9D6gRCvCG44T4WiPvfH7hL3AD8YbA3D%2FwcR9WXNu3%2FafU1%2FqO4BZdPIHgHkEVyP%2FDvrsBsfpeNSeI08H%2FReoAh4Ha0RaV1MWgCnx0dFreMjF3T2rLPAE9eNaJYUTBKpPSRvo%2FveUglqVarzM5Mcey5p3n2mSdZWJihLZUkk0ljmib5%2FBIL84tquVymWFxSxUJBFUsFVSmXlefWleM6frVScSqVsuO6ruv7yg2HbTcejzupVKpmWVZdCOEqpbxIJOKHw2HV2dkpa6fmKxK0scDQ6sXNRHspZfP%2B64jq4kC%2Bpy0sZmBZrSzXsE6dob1cYCjsMV%2BH%2FHKIR2ttyHAbabNEt1nmrBfmzu4SUUPywEI7PRmbAz0hEpYkWS6TKpfYF1I40uPpZ6v0t1tkYoJERDTns%2BnyQ25DorD4JJDTPwz3cnQLkmj9AeF%2Bjoge4Ld39EfwQuM%2BYSB5GEEsaLl7S5Jo4BNqmbeLO8nwUrSy82c5LP6Kh9Tf7XQqgdLxA2uaJfDHwAfW6iBGh8W%2FAT4LdLU0HwI%2BBPzmTufxPFAG3jkypv47XCWJoqFg9H1drLhWLWNaYYRRoby8zOzsDKdOHefZZ55kemqCcMgiEY%2BqQiFPpVrF96SqVGtyebnoFwsFr1qt%2BI7n%2BgLlJeIJP5VKupaZqHqeX%2FV931HK92zb9hOJhN%2Fe3u5Vq1XP9325tLSkAJXL5Th8%2BLD6k68ckWAEuSEC8%2BhGFg%2BltId3oMc4ZFWYcn2kYWLj4%2FmK4kKJ%2BW88A57PvhSUDUHRFXxj0aIeM3lRd4SQGSF1vEgmWmXZE1SVYLlQI9EeI2wJBktLdBaXiEqtPwsZMJ93WSh5uL5EKgNjy%2BWHTnCjtpIo7hKvRvAzACi%2BSZ6PXNaPGeZe6rwNeNHl%2Fh284LjIWxG8Ntj7W46q0W0f%2B4jyuUscRvENwETwYWDHRIHWS6y9v353ZEz9l406j4ypfxgdFncA32H1Mu%2FXR4fF%2FSNjavx5zGUn%2BMMGScBVIArf93Ech0qlokqlIouL89RqdSx7jlrdYWZmmrNnTnP27Ck1M30Rz3VUMpmUvu%2BpxcUFhRBSSeUJYfhK%2BfXl5eWq67p10zQ90zSdUMj2crmcF4vF3IWFBWd8fNyv1WoynU6rbDbLoUOH5Je%2F%2FGUeeeQRJYRgo5Kc64LCNlAaWgacJslrWOLWWJWpfAHXsokrj7zjUVRhqpi4nuJCVfDOJ6K8phvetcfhpsQMX0VRTGUpRyPkix5fdm1%2BtL3OsbLFkA1Jt0quVGiSBEDdh6GIT6Xua%2Bct1XAz33iOUiqU9EBtIVFIXtkUaAUfuGyt%2Fv2qzhHx%2B8DnLuu4awHBy5vbRovVZLt4UH2bI%2BILwE8DN3FExDi6sdlxK4wOCxN41Zrmx9DSxKYYGVMnRofFB2EVmRtoc%2BgLTRRHW3euKFFkMhkMw1Ce58mFhQV55swZf35%2BQRiGvqHK5bLOLzF1UeWXlpQQUkYiEa9cXq67ruNGIhEVDoc90zSdtrY2N5Foc2KxWNVxnHokEvFd1%2FXi8bhMp9Myk8morq4umclkuPfee5tsoJTi8OHDze3N0eJHseYGNIQiEhLUMxm%2BemGGOzJ1fso9wz%2B5WfKGol6o4GfDzPXv4bFTRUQN3vKqCD2ZEF%2Bq%2BVQqU9wSmuez0zHa4zG6qwt8sxymo1eSiJpEbJO22jKxNXkkFh24WIF%2BX%2BBLE53Ut%2BGoJbUzVjBXIXRyHKRCiC2IQnBr49IAj%2B7oh935cS80bgve68h1HpPbxeNoojDQlpGdnPtN0Fz%2BNPBXI2PbIuk%2Fh3VS323A53cwj52iODKm5lsbriRRqP7%2BfuU4juu6bnVxcXE5n8%2FbQhhI6eP72rxZr9epVCo%2BKD%2BdTnupVKruum65Xq%2FXfd%2F3bduW4XDYS6fTfjab9S3L8mZnZ%2F3Ozs7AMwpqtRqHDx9ussA991zew0MIAYYZFP8FpVqSTRp6PxKyGMzF%2BMpEN%2B3FCW5K%2BfxIaIH6IDxbnuJCNcKNgwkGuyJYpqAtahENC5arHv3npni6YDPpuGQyMQQL1B1JwlTc3V3EqfgIf%2F3fzIeeENQNi2xSh54LQye50S4VJg3JokkWhvbkbKTB2QS3BO8nL8sRqhVH1WmOiCW%2B9yMjNSkKnuZBtbNsPoqnWlSKt7AzothIgfn4dg4cGVOLo8PiPFrJ2cBtm%2FXfBnbitruwtuGKShQHDhyQCwsLtUKhsDgzM1NfWloyy%2BWykFJiGAaxWIx4PK7a2lKeUsrNZDJeR0eH47puvVQqeUIIGeTCVJ7nqc7OTrW4uKg6Ozu599571dYSwvahQGfdFiY6Ya5CqUbYuTZLGoZBR1uYQzfkeOAJePH8LEf2VAkbcGvS4ZbScc5V00zn%2BjAzSUKWwDAUycoyQ6LC54ppYimTeEcUf8IgbiruO5fmrdkyrzWXoCWHTt2Hx%2Bbh8xdMXnUozL7uKPGojWmuxHzod1ZIQgVE0eJE4csNGWNv8D79PC%2FbJFeDKNQGZU52gvuEAXQEY%2B483kJxbiVfYfPaXS7SG7RdzhJmLaH3XKL%2FWumlFfHL%2BN5NccWIQinFfffdp8LhcF0IIYvFYmVqakoUi0UBOvNVMplUHR0dJJNJ6bquF4lE5ODgoKxWq3J6elrddNNNAOuWEnD5UsNG8HxdJ1in5jdbEuGuwAhuSENAKCzY2x3jR17eyZOnw%2Fzis4vsVyXeMeRwMKXY6%2BXZM5XnwlyKqc4%2BRDzKSy6eAeCpZZvX7gnTkYkylUyS9hyyMcWN8fWSxNNL8NGnBB1tIV66J05fe5hwyGp6ZzbJIlC%2BNsjCMBsJ9fQZOJ6yhRCmUqtE3OPoNe7NbKa0uRTeLkJkOLjp54Jqc9skelljC9ovez4b4R4lOSKeRUsBN%2B14HJO9zT8Ig5M7HGWjyvK3bNK%2BCqPDIozOhdGKJ1q2SxsclmV1AFsrUpu0XxauqERxzz33qPvuu0%2FG4%2FG6aZqOUkpUKppI0%2Bk0qVSKjo4Ouru7VXt7u2ocs%2Fbv90qQwkYo1bEy0Ua%2BDCuo8tXIkt0MJtXvAixMEjGT%2FX0m2VSI%2Fb1xTl5c5n3nS7w1PMdrOySH0opBr8jgZJEaJhH0Pfq7%2FXmSskJ00uAlHS6d7TVMUV03J1fCN2bg0SWLV78oyqH%2BOKlYCFOvNJrKzBVdxQppaOX8SkiY42MBIaD1i55AE0Ub72Q%2FcOqyL1yaF6O9BjeGYrLlKdwFHLuM0bsv3WWbEDyJ4hZgL%2B8VKe7fQdU0xUta9r6zw5k8hjaFtkp4b6LViWxz3MH6a926%2FJlnPbJbjNexje%2B8JK641eOee%2B5RAEKIVU%2BuyclJJicneeyxx4DVisYrtaTYCkII8z%2F9GDbRFR1F0%2FIhdNCWJEilqYJ6HsF%2BJGSRazNoS4QY6orzooEk3zoWZXJighcvuRzKCl7SppokAfBDKRdwoWEt30C8lgqqHnzutGBvZ4Tb98foyUawbFOLNA1igI0lCyNYOgVmXC8gCiGEp1SwRtcuyxomP8pOiELwxkv0mGrZvpSYvIJfEmlCV0Y0BkDyZHCdBXVeD1x%2BSjrFK4IxfOo7U4iOjKnS6LA4Bry4pflnR4fFQyNj6v9tdlwgTdy%2FwUffbNme2%2BDz1wNf3GTYt15qvtvB9mv1XSZaIzo3el0D2DVXE6NecegKXga6aI8K3lFBdKqhSwgqYaAQmJZJOGTRmYlw05423viyLpYH9vK1Yoyn5xVfmd7%2BQnuxBn97Hp5agotV6EpZfPw2l9tSPlKCVFp%2FYhgmhmFhNrdNTMMK3s0gJylBBXjwJBb6abTyAJB8DZoeWR8KHKi2j%2FeIPuCDW%2FYRLToBwfC2xw7x7y9rLpeCwT%2Bxcq4f45fE5bmd3yXuQPATACge4%2BHn5RH5iQ3aHh4dFi%2FfoJ3RYZEC%2FoL1%2FirfZLVkM7PB4e8cHRbr9BSjw%2BIlwF3bm%2B7WuOZh5i8gTNdHVwlGE4GWLLQSExXEiRmAXKnJIRrrEpTOPCUFkbBgsDOObRo8HrP4wvE59hTz3JB06d3k%2BaiA00U4tgTLLkzXBN9eMvjVG30%2B8xoPw5T8fcGl4FXZN2SRS9pYVnAgLTkq1MpSwzAtTWoB3%2FsSE%2F2bWkIIQykleUg9wxHxceA3gDSKTwFv2%2FZV8%2FkE4hLr3CW%2BQ4ZxdGTkCO8Vv35Jsf%2FtwiTDL297HtvBg%2Bo7HBH3A78GHCTEB4D3b%2BvY94owiofQsp9ENCMnd4rGdW4lzkHg66PD4mPoGJDvAv3opeFvA3vWjFEBfn6NWfVJ4CQ6yreB7mDc3wzGHEBLGfewsWL1snFdEIXQ5o0mUQRGUbS5UW8bwVMZia7H0VTGN%2ByTIlAgaj1GNGTQn4vSqSrk0gM8eTrJgxOz%2FOdDy3z6GGRCEA6uri9hugJTVcFMTXC2anHRD3NzTvD5GY%2BapzjuRakg6O7yyVUk2YTAFtqPAlaUmKvOK5AoGnMPrB4WOk5AFzoFcPg9QrwZeDGCn%2BSI%2BDvg3RxVU2wGnSL%2FKIKfDFpcNtNTPKJ8DosHEHwUSFLnyxwRP8ZRtdF6Gt4rUmR5BLVKNL9SeD86bHs%2F8NscETYOH9xSOjgiBtHu04cAEDzAg%2Bp5%2BY6MjCk1OizuBJ5i9c1qo0Py37eNYX5rZEyd2GDcP2N10BvAzcDY85jylrguiAJ905ieDCQKwUqot7Gij2j2bDyjFYGrdEuJr%2BCGNdw6hlRk3TI37umjtyPGubMh4BgPnovhWTaxsBkoTrXgYhgC0xa0pS3e0BFlsDNKXsDUTBnLEPRGLPpyETKpCHbIxjADpzDYMDmNCCSKholUKX2ewWtlLfSwqvFu8Q4Mvoo2pb0FeJq7xPsx%2BEe6OMk9gXvnu8QQFq9H8RFW9A2fQNvyX73pFRZ8Gp2%2FYAD9hPwad4nfQPHNJmG8S2QxeBWCj6AjJGfQWvydZLveGEdVhbvEnSi%2BiHaF%2Fi1CvJW7xO%2Fg8i3%2BXOllkg5F3xcslf4IaAtGOEllm1LIJTAypiZGh8XbgL9kdWKYS8EDPjIypj65yecPA7%2FA9v0rgj%2FineN6IQoBiEaCq2b9TqFL%2Fa1YPETTiiAb4n5AJKppXRD4nkd08gLZpTlS9RLp%2BSmkbXN7yKXsmOwdynLzgTayiRCuJ1GAbQnCtkE0bBIJGSSjNsmoiQLyQylcT2Fb0Ba1ScZNbNNoropYRxJ6X5gmhhCoYOkhVTO9RZAUtAWfVo9xt3gpPkfRGvgsik%2FhA5OUOSKOA0OYtLP66%2F6SXt7LJF%2Fb8gofVQXeLV6B4PNBvMWLUIEy8Yg4CziYHGyZl4%2FgThQf5EoSBcCD6qvcLW7B5zPA64AbUfwvLOCImAfOcZiDQGrNuT5MmF%2FjqFqf42KHGBlTXwl0BR9DZ4y6FL4D3DkypjZ10AqUpa9H6zT%2B7RZjuegkN6%2BEQPeyQ1xXRLGyY6zcfEIQ5KkKOgmUUEFhYu3wJAyaegqkoK4EeTdEuRonUvE5rtooZrpQyufCYoUDfRa33dBOVyYCSuIrgSnAsgwsi6B%2BiX5XSpJJRPCDUoKG0ahvorTeBLXps8BYs%2FRQK%2Be58RGfVGeAYY6IX0DHHTRMZ3HWP50WgN%2FgqPpvABzZxgPp02qGt4t%2FTYYH0JmrGsrytY5LU8DP8aD6R46IrRWlO8Un1XGEuIPD3A38ASsSQwfrTYYXgSMc3bJwTx34ypq249uZysiYKgLvGh0Wf42%2BYV%2BG9qtIAD7wHNqk%2BnXg4ZGxS3uVjoypyuiwGAHuRmenehUrkafTaF3F%2B0fG1LdHh8Xvs3L%2BwDr%2FkO%2Bikwc3sM5hTVwjC8QLCiFEGEi847W88tZB4%2F8Mv%2FlnaB%2B6GSd%2FDrc8H%2FRp4Q7V0FOo5j40nu6CmuuxeHGBR8%2BUiCxMM2skSHdliUVNpIRsMsSBviRtCTuoBcKK74PRiDJZj0aC3vUSxCbnZZgYoQT1coEvfukfOLfAB%2F70S%2FJzwDJQUGrTdGpa9D7CgcDv4GYEL0ZSQjCNzkD9JY62%2FMG%2BR9wKtOFR49PbSKbyDhEnzG0YvBydr1JgcBHJVxB8sTn2EXEbBikkVY5uohc4ImIYgVu0x%2FkNk%2B1sBa04vQHFLQhuRnEAwSngcXye4M85%2FUKb4oJclnuAqZExtd7BZmdjDgDlkbENEvQ8T1wvRBECEr%2FwOl55y4Dx929880%2BTG7yZen4cWVkEoYJCwATOi40I00CJKVTz5lVK4Lo%2BS8sOZ6eXmVqokorZ9HZEScVDhExBOGwSC5mYho7XWM1Aq2ZGUwfRbGsoLzd%2Fgjd8SYUwwY5qovji33N%2Bgd%2F7kxWiKG5JFLvYxWXgell6SEDWHZ1f0fddUD5mNI1pR2m9WfU9HSg61cpTvqkrUBJTQXvcI9rmMlRxiYZN4tEQIbtRdFgvW5p6jYaJs5ljszEWTduECv6teFq2MopqjtG0gUiauT9VEKrueiw3znXlJHaxi%2BeP64ko%2FL%2F5JidfsU%2FMnj3%2BTK5z8KXCNCMoK6xjJ4IbsXHL0uIB2Ug%2F14AhFVZYEYlJ2lI%2BpiEwgkhPPYxs%2Bj00ZYZgOWOimiZYoUA2dBGqlSBWTKI0UnIpQEgtTShQhp6XUpK52WkA%2BehZ%2BS9ojbnPLlHs4griuiAKpZQUQniAV66p%2F3H%2B%2FJm7q3%2F7WTq7OoMq4q1ifsPBClByJfCjISGoRn%2FVekTz%2F8YnQptOgixZLRILjYhQmqUAN7RdrZpCoGxdiaegobosFIqMj49Trat%2F%2BvZZZtFE4Sm1VTabXezi8nBd6CgAhC6THo9B7H0%2FZdybivCLVyS8%2BXsAlbr65wf%2BUf3y9CIFtDdfRSlVv9bz2sUPDq4bogAQQsSAKNoRJ8xWEZHf%2B1Do%2Bftoe7mDrndRRRPFrkSxiyuG62Lp0QKHFa9FBc1wz%2B9XtvTQuggPfW51wNkliV1caVxXRKGU8oQQDZG8lSi%2BH28sgf79%2FODlAPVdk%2Bgurgauq6VHA4Ffhc1K8NRVqTd5ldFw125IFO4uSeziauG6JAoAoSseW%2Bgn8%2FcjUTSWUBLw16S%2F28Uurij%2BP4dVQiup8F5NAAAAAElFTkSuQmCC',
//send: 'data:image/gif;base64,R0lGODlhEgASAIQDADs7OxBpAI6OjjH%2FAMLCwtHR0dbW1tvb29zc3OLi4uPj4%2Bbm5urq6uzs7O%2Fv7%2FDw8PLy8vX19fb29vf39%2Fj4%2BPn5%2Bfr6%2Bvv7%2B%2Fz8%2FP39%2Ff7%2B%2Fv%2F%2F%2FzH%2FADH%2FADH%2FADH%2FACH%2BEUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAB8ALAAAAAASABIAAAV74CeOZGmeaAqsq8pGDqMA53oojERVEkSXAANi4ZhcBEjM5jdaMZDIzUYjZTYll4wUKsBYRQAMd2ORLk0AqtmCDLi%2FH8BmvAkMAvC0emu%2FB4ATAm6DbgN%2BJAAPE32GjYZ%2FiAyEg4%2BQTQcJDWUAfW5oBAUsH3aWQKIigyMhADs%3D',
//sendMessage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKLSURBVDiNfZPNbxNXFMV%2Fd2bMjDtYiZMYJRhivgoxiRtACV8SXXRLhdQdUrsICzb9D5DYVwWxqopYtkLdFLGBVt1WbVWh7mhiywgFMFEkKLKdEI8%2F5uPdLpwEVTE90tu8d9%2B595zznqgqg9BqncqJRCcskmlgHEbVUK84sf2nO7S4vFUngwiC4KMSxlwU0QNgJrq16iUAt1D8TdA1FetH3y%2F%2FAODs7Dw7I8bkxTJvgdlurXo2e7yDiEejLB97halVUd0TbMyk%2FMzSd%2F%2BZoNmcG3JTncvdWuXO1t7Q1F9q2%2FMCoLqmzUpWts52FYolVHV7tVrTJ%2BpLaBz%2B%2FMyY0Oj%2FIImfhPUl7smNWzc%2BtYW7URwNFyZXye99zfTIfbLHI0R2KNxGoyxE2flHjkmi7xeuXB3OjeUIw19Q85hg%2BT5h7zq73K8QkR2Xk6TSN7vjzVnGmBHPdXn40wNsywdsvAOfEyx%2FjZrmwO7r1WncQokg8BzLsiw%2B8H0OHTrMr78vYVkZRDL9jK3MeyUIWV79k8MCEBH25fPkxk7yePEltjXeL5IUqh0aZaFRFkzy5h2BTPCilt98Bwq2Y7M3f4SVlZNUqn9QnLpGo9zX7xVKQMRadQ8A7uSXWM4nNBpP%2BgSqSpzE9LodOt1xDk5%2Bxt%2BL99jtf4HrbuDUOwwP%2BcRjR0mlRuhF53DTl4CbmwQo7aBFc73J%2BTMXaLcDPG8%2FIk%2Bx5DnQwLY9RvwPefrMJZ0%2BQmbTnj6BUZLEMFOc5e3GOiZJQLIop0l0vh9dDL0IisdGeV5bRtX0CZyUU0%2BSeHT%2FvgJRFJL20ttG7fxmShj2OFg4TLvTxkk5dccYWfj29jd34yQefm9mA%2BDYzppBFv4FO4Au%2FTAT%2FmYAAAAASUVORK5CYII%3D',
//zoom_in : GS_HOST + 'images/zoom_in.png',
//zoom_out : GS_HOST + 'images/zoom_out.png',
};
/* translations */
$.gctour.i18n.de = {
'name': 'Deutsch',
'general': {
'save': 'Speichern',
'copy': 'kopieren',
'cancel': 'Abbrechen',
'close': 'Schließen',
'edit': 'bearbeiten',
'rename': 'umbenennen',
'delete': 'löschen',
'load': 'laden',
'install': 'Installieren',
'findMe': 'Finde mich!',
'exampleCoords': 'z.B. N49° 26.082 E7° 46.587 oder 49.434701 7.776446',
'notLoggedIn': 'Sie müssen sich auf einer Seite von geocaching.com befinden und dort angemeldet sein.',
'pleaseWait': 'Bitte warten - Daten werden geladen...'
},
'container': {
'toolbar': {
'newList': 'Neue Tour erstellen',
'openTour': 'Eine Tour laden',
'downloadTour': {
'caption': 'Tour runterladen',
'webcodeDownloadHelp': 'Bitte gib hier den Webcode an, den du von deinem Freund bekommen hast und drücke dann auf "Tour runterladen".',
'webcodeerror': 'Der angegebene Webcode existiert leider nicht!',
'webcodesuccess': ' wurde erfolgreich geladen!',
'webcodeOld': '\n !!ACHTUNG!!\nEs handelt sich bei diesem Webcode um eine alte Tour. Um sie auch mit den Vorzügen von GCTour 2.0 nutzen zu können musst du sie bitte jetzt erneut hochladen.'
},
'showSettings': 'Einstellungen anzeigen',
'saveSettings': 'Touren und Einstellungen sichern'
},
'tourHeader': {
'sendToGps': 'an GPSr senden',
'downloadGpx': 'GPX downloaden',
'saveAsBookmarklist': {
'title': 'als Bookmarkliste speichern (PMO)',
'caption': 'Tour als Bookmarkliste speichern',
'success': 'Tour als Bookmarkliste gespeichert',
'up-to-date': 'Die Bookmarkliste der Tour ist auf dem neusten Stand',
'duplicate': 'Die Bookmarkliste dieser Tour wurde bereits angelegt',
'added': 'Der Bookmarkliste hinzugefügt',
'removed': 'Von der Bookmarkliste entfernt'
},
'makeMap': {
'caption': 'auf Karte anzeigen',
'wait': 'Verfügbarkeit der Karte wird getestet.'
},
'upload': {
'caption': 'Tour hochladen',
'tourUploaded1': '<br>Tour erfolgreich hochgeladen! Der Webcode lautet:<br><b>',
'tourUploaded2': '</b><br><br>Die Onlineabfrage kann unter <a href="' + GCTOUR_HOST + 'tour/xWEBCODEx" target="_blank">' + GCTOUR_HOST + 'tour/xWEBCODEx</a> geschehen.<br><br>',
'tourUploadPMO': 'PM Only Caches (nicht zu verarbeiten)',
'tourUploadRetracted': 'Caches mit ungültigem GC Code (nicht zu verarbeiten)'
},
'addOwnWaypoint': 'eigenen Wegpunkt hinzufügen',
'sortAlphabetically': 'alphabetisch sortieren'
},
'cacheList': {
'logYourVisit': 'logge deinen Besuch',
'moveToList': 'in andere Tour verschieben',
'removeFromList': 'aus Tour entfernen'
}
},
'settings': {
'caption': 'Einstellungen',
'language': {
'header': 'Sprache',
'select': 'Sprache wählen'
},
'printview': {
'header': 'Druckansicht',
'printMinimal': 'Minimierte Druckansicht',
'printMinimalDesc': 'Beinhaltet nur noch Hint und Spoiler zu jedem Geocache.',
'logCount': 'Anzahl der Logs in Druckansicht',
'logCounts': ['keine', 'alle', 'anzeigen'],
'fontSize': 'Schriftgr&ouml;&szlig;e',
'decryptHints': 'Hints entschlüsseln',
'decryptHintsDesc': 'Die Hinweise werden schon mittels Rot13 in der Druckansicht entschlüsselt.',
'editDescription': 'Beschreibung editierbar',
'editDescriptionDesc': 'Die Beschreibung lässt sich komplett nach eigenem Belieben anpassen.',
'showSpoiler': 'Spoiler Bilder anzeigen',
'showSpoilerDesc': 'Es werden die Spoiler mitgedruckt.',
'additionalWaypoints': 'Eigene Wegpunkte anzeigen',
'additionalWaypointsDesc': 'In der Druckansicht findet sich eine Tabelle mit allen "eigenen Wegpunkten".',
'additionalWaypoints2': 'Eigene Wegpunkte auf Titelseite einsortieren',
'additionalWaypoints2Desc': 'Eigene Wegpunkte werden auf der Titelseite in die Cache-Tabelle einsortiert. Ansonsten wird eine eigene Tabelle angelegt.',
'loggedVisits': 'Log-Counter anzeigen',
'loggedVisitsDesc': 'Eine Übersicht wie oft der Geocache schon gefunden wurde.',
'pageBreak': 'Seitenumbruch nach Geocache',
'pageBreakDesc': 'Es wird nach jedem Geocache eine neue Seite angefangen. Sieht man erst beim Ausdrucken.',
'pageBreakAfterMap': 'Seitenumbruch nach Übersichtskarte',
'pageBreakAfterMapDesc': 'Es wird ein Seitenumbruch nach der Übersichtseite hinzugefügt, um das Deckblatt abzuheben.',
'frontPage': 'Titelseite',
'frontPageDesc': 'Es wird eine Titelseite erzeugt mit allen Geocaches, Index und Platz für Notizen.',
'outlineMap': 'Übersichtskarte für alle Caches',
'outlineMapDesc': 'Auf der Titelseite wird eine Karte mit allen Geocaches in der Tour angezeigt.',
'outlineMapSingle': 'Übersichtskarte für jeden Cache',
'outlineMapSingleDesc': 'Unter jedem Geocache erscheint eine Karte mit seinen "Additional Waypoints".',
'showCacheDetails': 'Cache-Listings und eigene Wegpunkte drucken',
'showCacheDetailsDesc': 'Alle Geocache-Listings und eigene Wegpunkte der Tour werden mitgedruckt (wenn deaktiviert, wird nur die Titelseite gedruckt).',
'printCacheTableD': 'Spalten in Cachetabelle auf der Titelseite auswählen',
'printCacheTableDDesc': 'Auf der Titelseite können hier Spalten der Cachetabelle ausgewählt werden. Nur ausgewählte Spalten werden gedruckt.<br>'
},
'map': {
'header': 'Karte',
'type': 'Standard Kartentyp',
'types': {
'OSM Mapnik': 'mapnik',
'OSM DE': 'osmde',
'OSM Fahrrad': 'osmaC',
'OSM Outdoors': 'osmaO',
'OSM ÖPNV': 'osmaP',
'Topo Deutschland': 'oda',
'Outdooractive': 'odan',
'Google Karte': 'roadmap',
'Google Satellit': 'satellite',
'Google Hybrid': 'hybrid',
'Google Gelände': 'terrain'
},
'size': 'Standard Kartengröße',
'geocacheid': 'Geocache Code anzeigen',
'geocacheidDesc': 'Es wird immer der GCCode (z.B. GC0815) mit auf der Karte angezeigt.',
'geocachename': 'Geocache Namen anzeigen',
'geocachenameDesc': 'Der Name eines Geocaches wird zusätzlich mit eingeblendet.',
'geocacheindex': 'Geocache Index anzeigen',
'geocacheindexDesc': 'Die Postion innerhalb der Tour wird mit angezeigt.',
'awpts': 'Additional Waypoints anzeigen',
'awptsDesc': 'Wenn aktiviert, dann werden die "Additional Waypoints" eines Geocaches mit angezeigt.',
'awpt_name': 'Additional Waypoints Namen einblenden',
'awpt_nameDesc': 'Der Name eines "Additional Waypoints" wird mit angezeigt.',
'awpt_lookup': 'Additional Waypoints Lookup einblenden',
'awpt_lookupDesc': 'Der Lookupcode eines "Additional Waypoints" wird mit angezeigt.',
'owpts': 'Eigene Wegpunkte einblenden',
'owptsDesc': 'Wenn du "Eigene Wegpunkte" mit in deiner Tour hast, so werden diese auch mit auf der Karte angezeigt.',
'owpt_name': 'Namen eigener Wegpunkte anzeigen',
'owpt_nameDesc': 'Zusätzlich kann man sich noch den Namen zu jedem Wegpunkt anzeigen lassen.'
},
'gpx': {
'html': 'Beschreibung mit HTML',
'htmlDesc': 'Manche Geräte/Programme haben Probleme beim Anzeigen eines Geocaches mit HTML-Formatierung. Wenn du nur noch kryptische Beschreibungen siehst, dann bitte diese Option deaktivieren.',
'wpts': 'Additional Waypoints exportieren',
'wptsDesc': 'Additional Waypoints werden als extra Wegpunkt mit in die GPX exportiert. Damit hat man jeden Parkplatz direkt auf dem Gerät.',
'ignoreParkWpts': 'Parkplätze nicht exportieren',
'ignoreParkWptsDesc': 'Wenn du Parkplätze nicht benötigst, werden sie nicht als extra Wegpunkt mit in die GPX exportiert.',
'ignoreEqualListingCoordsWpts': 'Wegpunkte mit identischen Listing-Koordinaten nicht exportieren',
'ignoreEqualListingCoordsWptsDesc': 'Wenn du Wegpunkte, deren Koordinaten mit den Listing-Koordinaten übereinstimmen, nicht benötigst, werden sie nicht als extra Wegpunkt mit in die GPX exportiert (diese Wegpunkte können Informationen, die zum Finden des Caches notwendig sind, enthalten).',
'wptAsGeocache': 'Wegpunkte als Geocaches exportieren',
'wptAsGeocacheDesc': 'Wenn die Wegpunkte als Geocaches exportiert werden, kann für diese auf Garmin-Geräten der Geocaching-Modus verwendet werden.',
'attributesToLog': 'Cache-Attribute als erster Logeintrag',
'attributesToLogDesc': 'Cache Attribute werden zusätzlich als erstes Log eingetragen.',
'stripGC': 'Entferne "GC" in GC-Code',
'stripGCDesc': 'Alte Geräte haben Probleme mit Wegpunkten, deren Namen länger als 8 Zeichen sind. Wenn du so ein altes Garmin hast, dann bitte diese Option anwählen!',
'maxLogCount': 'Maximale Anzahl der Logs',
'prefixFavScore': 'Zeige den Prozentwert der Favoritenpunkte vor dem Cachenamen',
'prefixFavScoreDesc': 'Im Geocaching-Menü des GPSr wird vor dem Cachenamen der Prozentwert der Favoritenpunkte angezeigt (z.B. "8%GC10000 - A New Beginning"). Damit lässt sich leicht erkennen, wie ein Cache bewertet wurde.',
'gsakWptExtensions': 'GSAK-Erweiterungen',
'gsakWptExtensionsDesc': 'Persönliche Cache-Notizen und originale Cache-Koordinaten (nur, falls die Koordinaten korrigiert wurden) sind zusätzlich in der GPX-Datei enthalten. Diese Datenfelder sind GSAK-spezifisch, können aber auch von einigen anderen Anwendungen verarbeitet werden.'
},
'theme': {
'select': 'Theme wählen'
},
'exportimport': {
'export': 'Export der GCTour-Einstellungen (inklusive aller Touren)',
'import': 'Import der GCTour-Einstellungen (inklusive aller Touren)'
}
},
'autoTour': {
'wait': 'Bitte warten - autoTour wird erzeugt!',
'radius': 'Radius',
'center': 'Mittelpunkt',
'refresh': 'Vorschau aktualisieren',
'cacheCounts': 'Geocaches',
'maxCaches': 'Geocaches: ###NHITS### | Maximum: '+AUTOTOUR_MAX_CACHES+'<br>Die ersten '+AUTOTOUR_MAX_CACHES+' werden gespeichert.',
'duration': 'Dauer',
'filterLabel': 'Filter',
'filter': {
'type': 'Typ',
'size': 'Größe',
'difficulty': 'Schwierigkeit',
'terrain': 'Gelände',
'special': {
'caption': 'Spezial',
'pm': {
'all': 'Alle',
'basic': 'Keine PM only',
'premium': 'PM only'
},
'notfound': 'Nicht gefunden',
'isActive': 'Aktiv',
'minFavorites': 'Mindestzahl Favoriten'
}
}
},
'dlg': {
'newVersion': {
'caption': 'Neue Version verfügbar',
'content': 'Es gibt eine neue Version von GCTour.\nZum update gehen? \n\n', //ohne Anwendung
'changelog': 'Neue GCTour-Version ' + VERSION + ' installiert. Übersicht der Änderungen: '
},
'error': {
'content': '<img src="' + $.gctour.img.sad + '">&nbsp;&nbsp;Leider ist ein Fehler aufgetreten!<br/>' +
'Versuch es einfach noch einmal oder suche nach einem <a href="#" id="gctour_update_error_dialog">Update</a>!<br/><br/>'
}
},
'notifications': {
'addgeocache': {
'success': {
'caption': '{0} wurde hinzugefügt!',
'content': '<b>{0}</b> enthält jetzt auch <b>{1}</b>.'
},
'contains': {
'caption': '{0} wurde <i>nicht</i> hinzugefügt!',
'content': '<b>{0}</b> enthält <b>{1}</b> schon.'
}
}
},
'units': {
'km': 'Kilometer',
'mi': 'Meilen'
},
'send2cgeo': {
'title': 'an c:geo senden',
'noWP': 'ohne eigene Wegpunkte',
'usage': '<li>c:geo auf dem Mobilgerät starten und in eine Cacheliste wechseln</li>' +
'<li><i>Menü → Importieren → Importiere von send2cgeo</i></li>' +
'<li>Die Tour (ohne eigene Wegpunkte) wird automatisch in die ausgewählte Liste geladen</li>' +
'<li>c:geo wartet noch 3 Minuten, um weitere Caches herunterzuladen</li>',
'overview': 'Übersicht',
'setup': {
'header': 'Setup',
'registerBrowser': 'Hier klicken um den Browser für Send2cgeo zu registrieren',
'desc1': '<li>c:geo auf dem Androidgerät starten, dann <i>Menu → Einstellungen → Dienste → Send to c:geo</i></li>' +
'<li><i>Registrierung anfordern</i> wählen, es öffnet sich ein Fenster mit einer fünfstelligen PIN</li>',
'desc2': 'PIN hier eingeben:',
'desc3': 'PIN senden und damit Gerät hinzufügen'
},
'register': 'Browser noch nicht für Send2cgeo registriert - bitte zum "Setup" Tab wechseln und den Browser registrieren.',
'senderror': 'Ein Fehler ist aufgetreten, bitte später noch einmal versuchen.'
},
'marker': {
'coord': 'Koordinaten',
'content': 'Inhalt',
'contentHint': 'wird in Druckansicht angezeigt',
'type': 'Typ'
},
'update': {
'dialog': '<div><p>Es ist eine neue Version von <a target="_blank" href="' + GCTOUR_HOST + '"><b>GCTour</b></a> verf&uuml;gbar.</p><p>Du verwendest Version <b>###VERSION_OLD###</b>, die aktuellste Version ist <b>###VERSION_NEW###</b>.</p><div class="dialogFooter"></div>',
'upToDate': 'GCTour ' + VERSION + ' ist aktuell!'
},
'printview': {
'found': 'Fund',
'note': 'Notiz',
'marker': 'Eigene Wegpunkte',
'removeMap': 'Karte entfernen',
'zoomMap': 'Diese Karte in einem neuem Tab öffnen.',
'dontPrintHint': '<b>Hinweis:</b><br/>Elemente in einem solchen Kasten werden <u>nicht</u> mit gedruckt!',
'reloadMap': 'Karte neu laden',
'print': 'Druck starten',
'mapHeight': 'Höhe',
'printWidth': 'Breite'
},
'cache': {
'addToTour': 'Zur Tour hinzufügen',
'directPrint': 'Drucke diesen Geocache',
'moveCoords': 'Verschiebe die Koordinaten',
'movedCoords': 'Die Koordinaten zu diesem Geocache wurden verschoben!',
'moveCoordsHelp': 'Hier hast du die Möglichkeit die original Koordinaten dieses Geocaches durch neue zu ersetzen. Diese werden dann in der Druckansicht, wie auch in der GPX verwendet. Praktisch bei der Lösung eines Mysteries.',
'deleteCoords': 'Koordinaten löschen',
'originalCoords': 'Originale Koordinaten',
'newCoords': 'Neue Koordinaten',
'addToCurrentTour': 'Zur <b>aktuellen</b> Tour hinzufügen',
'addToNewTour': 'Zu <b>neuer</b> Tour hinzufügen',
'shown': 'Angezeigte Caches',
'marked': 'Markierte Caches',
'all': 'Alle Caches'
},
'tour': {
'newDialog': 'Bitte gib einen Namen für die neue Tour ein ...',
'copy': 'Tour kopieren',
'copyNameAppendix': 'Kopie',
'delete': 'diese Tour löschen',
'deleteDialog': 'Soll die Tour wirklich gelöscht werden?',
'empty': 'Die Tour ist leer.'
}
};
$.gctour.i18n.en = {
'name': 'English',
'general': {
'save': 'Save',
'copy': 'copy',
'cancel': 'Cancel',
'close': 'Close',
'edit': 'Edit',
'rename': 'Rename',
'delete': 'delete',
'load': 'load',
'install': 'Install',
'findMe': 'Find me!',
'exampleCoords': 'e.g. N49° 26.082 E7° 46.587 or 49.434701 7.776446',
'notLoggedIn': 'You must be on a geocaching.com page and logged in there.',
'pleaseWait': 'Please wait - loading data ...'
},
'container': {
'toolbar': {
'newList': 'New tour',
'openTour': 'Load a tour',
'downloadTour': {
'caption': 'Download Tour',
'webcodeDownloadHelp': 'Please enter here the webcode you receive from your friend and click on "Download tour".',
'webcodeerror': 'The choosen webcode does not exist!',
'webcodesuccess': ' was successfully loaded!',
'webcodeOld': '\n !!ATTENTION!!\nThis webcode is connected with an old tour. To get all benefits of GCTour 2.0 you must upload this tour again.'
},
'showSettings': 'Show settings',
'saveSettings': 'Backup tours and settings',
'homepage': 'GCTour Home'
},
'tourHeader': {
'sendToGps': 'Send to GPSr',
'downloadGpx': 'Download GPX',
'saveAsBookmarklist': {
'title': 'Save as bookmark list (PMO)',
'caption': 'Save tour as bookmark list',
'success': 'Tour saved as bookmark list',
'up-to-date': 'The bookmark list for this tour is up-to-date',
'duplicate': 'A bookmark list for this tour already exists',
'added': 'Added to bookmark list',
'removed': 'Removed from bookmark list'
},
'makeMap': {
'caption': 'View on map',
'wait': 'Testing availablity of this map'
},
'upload': {
'caption': 'Upload Tour',
'tourUploaded1': '<br>Uploading tour was successful! Webcode:<br><b>',
'tourUploaded2': '</b><br><br>You can view the tour at <a href="' + GCTOUR_HOST + 'tour/xWEBCODEx" target="_blank">' + GCTOUR_HOST + 'tour/xWEBCODEx</a>.<br><br>',
'tourUploadPMO': 'Skipped PM only Caches',
'tourUploadRetracted': 'Skipped caches with invalid GC code'
},
'addOwnWaypoint': 'Add own waypoint',
'sortAlphabetically': 'Sort alphabetically'
},
'cacheList': {
'logYourVisit': 'Log your visit',
'moveToList': 'Move to another tour',
'removeFromList': 'Remove from tour'
}
},
'settings': {
'caption': 'Settings',
'language': {
'header': 'Language',
'select': 'Select language'
},
'printview': {
'header': 'Printview',
'printMinimal': 'Minimal printview',
'printMinimalDesc': 'This contains only the hint and spoiler of a geocache.',
'logCount': 'Number of logs in printview',
'logCounts': ['none', 'all', 'show'],
'fontSize': 'Font size',
'fontSizes': ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'],
'decryptHints': 'Decrypt hints',
'decryptHintsDesc': 'Hints will be already decrypted in the printout.',
'editDescription': 'Description editable',
'editDescriptionDesc': 'The description can be edited in the way you want it.',
'showSpoiler': 'Display spoiler',
'showSpoilerDesc': 'Spoiler images will be on the printout.',
'additionalWaypoints': 'Show additional waypoints',
'additionalWaypointsDesc': 'The printview will contain a table with all "Additional waypoints" from a geocache.',
'additionalWaypoints2': 'Add own waypoints to cache table',
'additionalWaypoints2Desc': 'Own waypoints will be added to the cache table on front page. Otherwise an extra table will be added.',
'loggedVisits': 'Show log counter',
'loggedVisitsDesc': 'This will show the "Find counts" overview.',
'pageBreak': 'Page break after cache',
'pageBreakDesc': 'After each geocache there will be a page break. Visiable after printing.',
'pageBreakAfterMap': 'Page break after map',
'pageBreakAfterMapDesc': 'It will be a page break after the overview to separate it from the geocaches.',
'frontPage': 'Front page',
'frontPageDesc': 'An overview will be generated containing the complete list of geocaches including index and space to take notes. ',
'outlineMap': 'Outline map for all caches',
'outlineMapDesc': 'The overview will contain a map with all geocaches.',
'outlineMapSingle': 'Outline map for every cache',
'outlineMapSingleDesc': 'After each geocache a map containing the geocache and its "Additional waypoints" will be shown.',
'showCacheDetails': 'Print cache listings and own waypoints',
'showCacheDetailsDesc': 'All geocache listings and own waypoints will be printed (if not set just the front page will be printed).',
'printCacheTableD': 'Select cache table columns on front page',
'printCacheTableDDesc': 'Cache table columns can be selected on front page. Only selected columns will be printed.<br>'
},
'map': {
'header': 'Map',
'type': 'Default map type',
'types': {
'OSM Mapnik': 'mapnik',
'OSM German style': 'osmde',
'OSM Cycle': 'osmaC',
'OSM Outdoors': 'osmaO',
'OSM Public Transport': 'osmaP',
'Topo Germany': 'oda',
'Outdooractive': 'odan',
'Google Map': 'roadmap',
'Google Satellite': 'satellite',
'Google Hybrid': 'hybrid',
'Google Terrain': 'terrain'
},
'size': 'Default map size',
'sizes': ['large', 'medium', 'small'],
'geocacheid': 'Show geocache id',
'geocacheidDesc': 'The GCCode (eg. GC0815) will be shown on the map.',
'geocacheindex': 'Show geocache index',
'geocacheindexDesc': 'The position of each waypoint in the current tour will be shown on the map.',
'geocachename': 'Show geocache name',
'geocachenameDesc': 'The name of an geocache will be shown on the map.',
'awpts': 'Display addtional waypoints',
'awptsDesc': 'If enabled, additional waypoints will be shown on the map.',
'awpt_name': 'Show name of the additional waypoints',
'awpt_nameDesc': 'The name of the additional waypoints will be shown on the map.',
'awpt_lookup': 'Show lookup code of additional waypoints',
'awpt_lookupDesc': 'The lookup code of the additional waypoints will be shown on the map.',
'owpts': 'Display own waypoints',
'owptsDesc': 'Own waypoints in the current tour will be shown on the map.',
'owpt_name': 'Show name of own waypoints',
'owpt_nameDesc': 'Display the name of your own waypoints'
},
'gpx': {
'header': 'GPX',
'html': 'Description with HTML',
'htmlDesc': 'Some programs/GPSr have problems to show geocaches when their description is HTML formatted. If you only see scrabbled descriptions then please disable this option.',
'wpts': 'Export additional waypoints',
'wptsDesc': 'Additional waypoints will be exported as extra waypoint to the GPX. You will see every parking space on your unit.',
'ignoreParkWpts': 'Do not export parking spaces',
'ignoreParkWptsDesc': 'If you do not need parking spaces, they are not exported as an extra waypoint in the GPX.',
'ignoreEqualListingCoordsWpts': 'Do not export additional waypoints with matching listing coordinates',
'ignoreEqualListingCoordsWptsDesc': 'If you do not need waypoints whose coordinates match the listing coordinates, they will not be exported as extra waypoints in the GPX (note that these waypoints may contain information necessary to find the cache).',
'wptAsGeocache': 'Export all waypoints as Geocaches',
'wptAsGeocacheDesc': 'If you export waypoints as geocaches, you can use the Geocaching mode on Garmin devices.',
'attributesToLog': 'Create log with cache attributes',
'attributesToLogDesc': 'Cache attributes are also included as a first log.',
'stripGC': 'Strip "GC" in GC-Code',
'stripGCDesc': 'Older GPSr still have problems with waypoints having names longer than 8 characters. Please use this option if you own such an unit.',
'maxLogCount': 'Maximum number of logs',
'prefixFavScore': 'Show favorite score in front of cache name',
'prefixFavScoreDesc': 'When in Geocaching menu of GPSr, the favorite score is shown in front of the cache name (e.g. "8%GC10000 - A New Beginning"). Thereby it\'s easy to see how a cache has been rated.',
'gsakWptExtensions': 'GSAK extensions',
'gsakWptExtensionsDesc': 'Personal cache notes and original cache coordinates (only if the coordinates have been corrected) are also included in the GPX file. These data fields are specific to GSAK, but can also be processed by some other applications.'
},
'theme': {
'header': 'Themes',
'select': 'Select Theme'
},
'exportimport': {
'header': 'Export/Import',
'export': 'Export of GCTour settings (including all tours)',
'import': 'Import of GCTour settings (including all tours)'
}
},
'autoTour': {
'title': 'autoTour',
'wait': 'Please wait - generating autoTour!',
'radius': 'Radius',
'center': 'Center',
'refresh': 'Update preview',
'cacheCounts': 'Geocaches',
'maxCaches': 'Geocaches: ###NHITS### | Maximum: '+AUTOTOUR_MAX_CACHES+'<br>First '+AUTOTOUR_MAX_CACHES+' will be stored.',
'duration': 'Duration',
'filterLabel': 'Filters',
'filter': {
'type': 'Type',
'size': 'Size',
'difficulty': 'Difficulty',
'terrain': 'Terrain',
'special': {
'caption': 'Special',
'pm': {
'all': 'All',
'basic': 'No PM only',
'premium': 'PM only'
},
'notfound': 'I haven\'t found ',
'isActive': 'is Active',
'minFavorites': 'min. Favorites'
}
}
},
'dlg': {
'newVersion': {
'caption': 'New version available',
'content': 'There is a new version of GCTour.\nDo you want to update? \n\n',
'changelog': 'New GCTour version ' + VERSION + ' installed. Overview of changes: '
},
'error': {
'content': '<img src="' + $.gctour.img.sad + '">&nbsp;&nbsp;I\'m sorry, but an error has occurred.<br/>' +
'Please try again, or look for an update.<br/>' +
'If the error persists, you can get support <a href="https://www.geoclub.de/forum/t/gctour.78798/page-999" target="_blank" id="gctour_update_error_dialog">here</a>.<br/><br/>'
}
},
'notifications': {
'addgeocache': {
'success': {
'caption': '{0} added successfully!',
'content': '<b>{0}</b> now also contains <b>{1}</b>.'
},
'contains': {
'caption': '{0} was <i>not</i> added!',
'content': '<b>{0}</b> contains <b>{1}</b>.'
}
}
},
'units': {
'km': 'Kilometer',
'mi': 'Miles'
},
'send2cgeo': {
'title': 'Send to c:geo',
'noWP': 'no own waypoints',
'usage': '<li>Start c:geo on your device and go to a list of saved caches</li>' +
'<li>Select Menu → Import → Import from send2cgeo</li>' +
'<li>The tour (without own waypoints) will automatically be downloaded from geocaching.com to the list of saved caches</li>' +
'<li>c:geo will continue to wait for more caches to be downloaded for 3 minutes</li>',
'overview': 'Overview',
'setup': {
'header': 'Setup',
'registerBrowser': 'Click to register this browser for Send2cgeo',
'desc1': '<li>Run c:geo on your android device and select <i>Menu → Settings → Services → Send to c:geo</i></li>' +
'<li>Select <i>Request Registration</i>, you will get an info window showing a five digit PIN</li>',
'desc2': 'Enter PIN here:',
'desc3': 'Send PIN and add device'
},
'register': 'Browser is not registered for Send2cgeo yet - please switch to "Setup" tab and register your browser.',
'senderror': 'An error occurred, please try again later.'
},
'marker': {
'coord': 'Coordinates',
'content': 'Content',
'contentHint': 'will be shown in printview',
'type': 'Type'
},
'update': {
'dialog': '<div><p>There is a new version of <a target="_blank" href="' + GCTOUR_HOST + '"><b>GCTour</b></a> available for installation.</p><p>You currently have installed version <b>###VERSION_OLD###</b>, the latest version is <b>###VERSION_NEW###</b>.</p><div class="dialogFooter"></div>',
'upToDate': 'GCTour ' + VERSION + ' is up to date!'
},
'printview': {
'found': 'Found',
'note': 'Note',
'marker': 'Own waypoints',
'removeMap': 'Remove map',
'zoomMap': 'Open this map in a new tab.',
'dontPrintHint': '<b>Information:</b><br/>Elements in such a box will <u>not</u> be printed!',
'reloadMap': 'Reload map',
'print': 'Start printing',
'mapHeight': 'Height',
'printWidth': 'Width'
},
'cache': {
'addToTour': 'Add to tour',
'directPrint': 'Print this geocache',
'moveCoords': 'Move the coordinates',
'movedCoords': 'The coordinates to this geocaches are moved.',
'moveCoordsHelp': 'You have the possibility to change the original coordinates of this geocache. These will then be used in printview and also in the GPX file. This is quiet handy if you solve a mystery.',
'deleteCoords': 'Delete coordinates',
'originalCoords': 'Original coordinates',
'newCoords': 'New coordinates',
'addToCurrentTour': 'Add to <b>current</b> tour',
'addToNewTour': 'Add to <b>new</b> tour',
'shown': 'Displayed geocaches',
'marked': 'Marked geocaches',
'all': 'All caches'
},
'tour': {
'newDialog': 'Please enter a name for the new tour ...',
'copy': 'Copy tour',
'copyNameAppendix': 'Copy',
'delete': 'Delete this tour',
'deleteDialog': 'Are you sure to delete this tour?',
'empty': 'The tour is empty.'
}
};
$.gctour.i18n.fr = {
'name': 'Français',
'general': {
'save': 'Enregistrer',
'copy': 'dupliquer',
'cancel': 'Abandonner',
'close': 'Fermer',
'edit': 'Editer',
'rename': 'Renommer',
'delete': 'supprimer',
'load': 'charger',
'install': 'Installer',
'findMe': 'Localisez-moi!',
'exampleCoords': 'p. ex. N49° 26.082 E7° 46.587 ou 49.434701 7.776446',
'notLoggedIn': 'Vous devez être connecté à geocaching.com, merci de vous connecter.',
'pleaseWait': 'Veuillez patienter...'
},
'container': {
'toolbar': {
'newList': 'Nouveau tour',
'openTour': 'Ouvrir un Tour',
'downloadTour': {
'caption': 'Télécharger un Tour',
'webcodeDownloadHelp': 'Entrer ici le webcode que vous avez reçu et cliquer sur "Télécharger le Tour".',
'webcodeerror': 'Le webcode saisi est inexistant!',
'webcodesuccess': ' a été chargé avec succès!',
'webcodeOld': '\n !!ATTENTION!!\nCe webcode corrrespond à un ancien tour. Pour profiter pleinement de GCTour 2.0 vous devez soumettre de nouveau ce Tour.'
},
'showSettings': 'Configurer'
},
'tourHeader': {
'sendToGps': 'Transférer vers le GPSr',
'downloadGpx': 'Télécharger le GPX',
'makeMap': {
'caption': 'Voir sur la carte',
'wait': 'Vérification de la disponibilité et création de la carte... '
},
'upload': {
'caption': 'Soumettre un Tour',
'tourUploaded1': '<br>Le Tour a été correctement transféré! Webcode:<br><b>',
'tourUploaded2': '</b><br><br>Vous pouvez visualiser ce Tour sur <a href="' + GCTOUR_HOST + 'tour/xWEBCODEx" target="_blank">' + GCTOUR_HOST + 'tour/xWEBCODEx</a>.<br><br>'
},
'addOwnWaypoint': 'Ajouter un Waypoint personnel',
'sortAlphabetically': 'Trier alphabétiquement'
},
'cacheList': {
'logYourVisit': 'Loguer votre visite',
'removeFromList': 'Supprimer du tour'
}
},
'settings': {
'caption': 'Configuration',
'language': {
'header': 'Langue',
'select': 'Choisir langue'
},
'printview': {
'header': 'Version imprimable',
'printMinimal': 'Version imprimable minimaliste',
'printMinimalDesc': 'Ne contient que l\'indice et le spoiler de la cache.',
'logCount': 'Nombre de logs à inclure dans la version imprimable',
'logCounts': ['aucun', 'tous', 'afficher'],
'fontSize': 'Taille des caractères',
'decryptHints': 'Decryptage des hints',
'decryptHintsDesc': 'Les indices seront décryptés dans la version imprimable.',
'editDescription': 'Description éditable',
'editDescriptionDesc': 'La description est éditable comme bon vous semble.',
'showSpoiler': 'Affichage des spoilers',
'showSpoilerDesc': 'Les images Spoiler seront visibles dans la version imprimables.',
'additionalWaypoints': 'Affichage des Waypoints additionnels',
'additionalWaypointsDesc': 'La version imprimable contiendra un tableau avec tous les "Waypoints additionnels" de la cache.',
'loggedVisits': 'Affichage du nombre de logs',
'loggedVisitsDesc': 'Affiche un récapitulatif des "Trouvé(s)".',
'pageBreak': 'Saut de page entre les caches',
'pageBreakDesc': 'Il y aura un saut de page après chaque cache. Visible seulement à l\'impression.',
'pageBreakAfterMap': 'Saut de page après la carte',
'pageBreakAfterMapDesc': 'Il y aura un saut de page après la vue globale du Tour pour la séparer des pages de caches qui suivent.',
'frontPage': 'Page d\'accueil',
'frontPageDesc': 'Une vue d\'ensemble sera générée et incluera un index avec des cases dédiées à la prise de notes.',
'outlineMap': 'Vue d\'ensemble de toutes les caches du Tour',
'outlineMapDesc': 'La vue d\'ensemble incluera une carte avec toutes les caches.',
'outlineMapSingle': 'Vue d\'ensemble pour chaque cache',
'outlineMapSingleDesc': 'Après chaque cache, une carte indiquant l\'emplacement de la cache et de ses Waypoints additionnels sera affichée.'
},
'map': {
'header': 'Carte',
'type': 'Type de carte par défaut',
'size': 'Taille de carte par défaut',
'geocacheid': 'Afficher l\'Id de la cache sur la carte',
'geocacheidDesc': 'Les codes GC (eg. GC1S5ZE) seront affichés sur la carte.',
'geocacheindex': 'Afficher les Waypoints des caches sur la vue générale',
'geocacheindexDesc': 'Les Waypoints seront affichés sur la carte.',
'geocachename': 'Afficher le nom des caches sur la vue générale',
'geocachenameDesc': 'Les noms des caches seront affichés sur la carte.',
'awpts': 'Afficher les Waypoints',
'awptsDesc': 'Si cette option est cochée les Waypoints associées aux caches seront affichés sur la carte.',
'awpt_name': 'Afficher les noms des Waypoints additionnels',
'awpt_nameDesc': 'Les noms des Waypoints additionnels seront affichés sur la carte.',
'awpt_lookup': 'Afficher les codes lookup des Waypoints additionnels',
'awpt_lookupDesc': 'Les codes lookup des Waypoints seront affichés sur la carte.',
'owpts': 'Afficher les Waypoints personnels',
'owptsDesc': 'Les Waypoints personnels seront visibles sur la carte.',
'owpt_name': 'Afficher le nom des Waypoints personnels',
'owpt_nameDesc': 'Affiche le nom des Waypoints personnels sur la carte'
},
'gpx': {
'html': 'Description des caches au format HTML',
'htmlDesc': 'Certains programmes/GPSr ont des problèmes pour afficher les descriptions au format HTML. Si vous ne voyez qu\'une description tronquée de la cache, désactivez cette option.',
'wpts': 'Exporter les Waypoints additionnels',
'wptsDesc': 'Les Waypoints additionnels seront exportés vers votres GPS. Les parkings conseillés seront visibles sur votre GPS.',
'attributesToLog': 'Créer connecter avec les attributs du cache',
'attributesToLogDesc': 'Attributs de mise en cache sont également enregistrés comme un premier signe.',
'stripGC': 'Tronquer le préfixe "GC" dans le GC-Code',
'stripGCDesc': 'Les anciens GPSr ont parfois des problèmes avec les noms de Waypoints de plus de 8 caractères. Dans ce cas cochez cette option.',
'maxLogCount': 'Nombre maximum de journaux'
},
'theme': {
'select': 'Choisir Theme'
}
},
'autoTour': {
'wait': 'Veuillez patienter pendant la génération automatique du Tour ...',
'radius': 'Rayon',
'center': 'Centre',
'refresh': 'Actualiser l\'aperçu',
'cacheCounts': 'Géocaches',
'duration': 'Durée',
'filterLabel': 'Filtres',
'filter': {
'type': 'Type',
'size': 'Taille',
'difficulty': 'Difficulté',
'terrain': 'Terrain',
'special': {
'caption': 'Spécial'
}
}
},
'dlg': {
'newVersion': {
'caption': 'Nouvelle version disponible',
'content': 'Une nouvelle version de GCTour est disponible.\n Voulez-vous mettre à jour? \n\n'
},
'error': {
'content': '<img src="' + $.gctour.img.sad + '">&nbsp;&nbsp;Désolé une erreur est survenue.<br/>' +
'Réessayez SVP, ou vérifier les <a href="#" id="gctour_update_error_dialog">mises à jour</a> du script!<br/><br/>'
}
},
'notifications': {
'addgeocache': {
'success': {
'caption': '{0} a été ajouté',
'content': '<b>{0}</b> contient désormais aussi <b>{1}</b>.'
},
'contains': {
'caption': '{0} n\'a pas été ajouté',
'content': '<b>{0}</b> contient <b>{1}</b>.'
}
}
},
'units': {
'km': 'Kilomètre',
'mi': 'Miles'
},
'send2cgeo': {
'title': 'Transférer vers le c:geo'
},
'marker': {
'coord': 'Coordonnées',
'content': 'Description',
'contentHint': 'sera visble dans la version imprimable'
},
'update': {
'dialog': '<div><p>Une nouvelle version de <a target="_blank" href="' + GCTOUR_HOST + '"><b>GCTour</b></a> est disponible.</p><p>Version installée: <b>###VERSION_OLD###</b>, version la plus récente disponible: <b>###VERSION_NEW###</b>.</p><div class="dialogFooter"></div>',
'upToDate': 'GCTour ' + VERSION + ' est à jour!'
},
'printview': {
'found': 'Trouvée',
'note': 'Note',
'marker': 'Waypoint personnel',
'removeMap': 'Supprimer la carte',
'zoomMap': 'Ouvrir la carte dans un nouvel onglet.',
'dontPrintHint': '<b>Information:</b><br/>Les éléménts ayant cette apparence ne seront <u>pas</u> imprimés!',
'print': 'Lancez l\'impression'
},
'cache': {
'addToTour': 'Ajouter au tour',
'directPrint': 'Imprimer cette cache',
'moveCoords': 'Ajuster les coordonnées',
'movedCoords': 'Les coordonnées de cette cache ont été ajustées.',
'moveCoordsHelp': 'Vous avez la possibilité d\'ajuster les coordonnées de cette cache. Ces coordonnées seront utilisées dans la version imprimable et dans le fichier GPX . Très utile pour la saisie des solutions des caches Mystery.',
'deleteCoords': 'Supprimer les coordonnées',
'originalCoords': 'Coordonnées initiales',
'newCoords': 'Nouvelles coordonnées',
'addToCurrentTour': 'Ajouter au tour <b>actuel</b>',
'addToNewTour': 'Ajouter à un <b>nouveau</b> tour',
'shown': 'Caches affichées',
'marked': 'Caches marquées',
'all': 'Tous caches'
},
'tour': {
'newDialog': 'Veuillez saisir un nom pour ce nouveau tour ...',
'copy': 'Dupliquer le tour',
'copyNameAppendix': 'Copie',
'delete': 'Supprimer ce tour',
'deleteDialog': 'Etes-vous sûrs de vouloir supprimer ce tour?',
'empty': 'Le tour est vide.'
}
};
$.gctour.i18n.nl = {
'name': 'Nederlands',
'general': {
'save': 'Bewaren',
'copy': 'kopiëren',
'cancel': 'Annuleren',
'close': 'Sluiten',
'edit': 'Bewerken',
'rename': 'Hernoem',
'delete': 'verwijderen',
'load': 'laden',
'install': 'Installeren',
'findMe': 'Vind me!',
'exampleCoords': 'bv. N49° 26.082 E7° 46.587 of 49.434701 7.776446',
'notLoggedIn': 'U dient ingelogd te zijn, gelieve in te loggen.',
'pleaseWait': 'Even geduld - gegevens worden geladen ...'
},
'container': {
'toolbar': {
'newList': 'Nieuwe toer',
'openTour': 'Een toer laden',
'downloadTour': {
'caption': 'Toer downloaden',
'webcodeDownloadHelp': 'Gelieve de ontvangen webcode in te vullen en klik op "Toer downloaden".',
'webcodeerror': 'De gekozen webcode bestaat niet!',
'webcodesuccess': ' is succesvol geladen!',
'webcodeOld': '\n !!AANDACHT!!\nDeze webcode bevat een oude toer. Om alle voordelen van GCTour 2.0 te benutten moet deze toer opnieuw worden geüpload worden.'
},
'showSettings': 'Instellingen tonen'
},
'tourHeader': {
'sendToGps': 'Naar GPSr versturen',
'downloadGpx': 'GPX downloaden',
'makeMap': {
'caption': 'Bekijk op de kaart',
'wait': 'Beschikbaarheid kaart wordt getest'
},
'upload': {
'caption': 'Toer uploaden',
'tourUploaded1': '<br>Uploaden toer was succesvol! Webcode:<br><b>',
'tourUploaded2': '</b><br><br>Je kan de toer bekijken op <a href="' + GCTOUR_HOST + 'tour/xWEBCODEx" target="_blank">' + GCTOUR_HOST + 'tour/xWEBCODEx</a>.<br><br>',
},
'addOwnWaypoint': 'Eigen waypoint toevoegen',
'sortAlphabetically': 'Alfabetisch sorteren'
},
'cacheList': {
'logYourVisit': 'Log je bezoek',
'removeFromList': 'Van toer verwijderen'
}
},
'settings': {
'caption': 'Instellingen',
'language': {
'header': 'Taal',
'select': 'Taal kiezen'
},
'printview': {
'header': 'Afdrukweergave',
'printMinimal': 'Minimale afdrukweergave',
'printMinimalDesc': 'Dit bevat enkel de hint en spoiler van een geocache.',
'logCount': 'Aantal logs in afdrukweergave',
'logCounts': ['geen', 'alle', 'tonen'],
'fontSize': 'Fontgrootte',
'decryptHints': 'Decodeer hints',
'decryptHintsDesc': 'Hints worden gedecodeerd bij het afdrukken.',
'editDescription': 'Beschrijving wijzigbaar',
'editDescriptionDesc': 'De beschrijving van de geocache kan aangepast worden.',
'showSpoiler': 'Spoiler tonen',
'showSpoilerDesc': 'Spoilers worden afgedrukt.',
'additionalWaypoints': 'Additional waypoints tonen',
'additionalWaypointsDesc': 'De afdrukweergave zal een tabel met de "additional waypoints" van een geocache bevatten.',
'loggedVisits': 'Log teller tonen',
'loggedVisitsDesc': 'Dit toont een "Find Counts" overzicht.',
'pageBreak': 'Pagina einde achter cache',
'pageBreakDesc': 'Bij het afdrukken komt achter elke geocache een nieuwe pagina.',
'pageBreakAfterMap': 'Pagina einde achter kaart',
'pageBreakAfterMapDesc': 'Er komt een nieuwe pagina tussen het overzicht en de geocaches.',
'frontPage': 'Frontpagina',
'frontPageDesc': 'Een overzicht wordt gemaakt met de volledige lijst van de geocaches met een index en plaats voor notities.',
'outlineMap': 'Kaart maken voor alle geocaches',
'outlineMapDesc': 'Het overzicht zal een kaart met alle geocaches bevatten.',
'outlineMapSingle': 'Kaart maken voor elke cache afzonderlijk',
'outlineMapSingleDesc': 'Na iedere geocache komt een kaart met de geocache en de additional waypoints.'
},
'map': {
'header': 'Kaart',
'type': 'Standaard kaarttype',
'size': 'Standaard kaartgrootte',
'geocacheid': 'Toon geocache id',
'geocacheidDesc': 'De GCCode (eg. GC2NTTG) wordt getoond op de kaart.',
'geocacheindex': 'Toon geocache index',
'geocacheindexDesc': 'De volgorde binnen de toer wordt getoond op de kaart.',
'geocachename': 'Toon geocache naam',
'geocachenameDesc': 'De naam van de geocache wordt getoond op de kaart.',
'awpts': 'Toon additional waypoints',
'awptsDesc': 'Additional waypoints worden getoond op de kaart.',
'awpt_name': 'Toon naam additional waypoints',
'awpt_nameDesc': 'De naam van het additional waypoint wordt getoond op de kaart.',
'awpt_lookup': 'Toon lookup code additional waypoints',
'awpt_lookupDesc': 'De lookup code van het additional waypoints wordt getoond op de kaart.',
'owpts': 'Toon eigen waypoints',
'owptsDesc': 'Eigen waypoints worden getoond op de kaart.',
'owpt_name': 'Toon naam eigen waypoints',
'owpt_nameDesc': 'De naam van het eigen waypoint wordt getoond op de kaart.'
},
'gpx': {
'html': 'Beschrijving met HTML',
'htmlDesc': 'Sommige programma\'s en GPS toestellen hebben problemen met geocache beschrijvingen in HTML. Indien dit het geval is, kan je best deze optie afzetten.',
'wpts': 'Exporteer additional waypoints',
'wptsDesc': 'Additional waypoints worden geëxporteerd als extra waypoint naar GPX. Alle paarkeerplaatsen zullen zichtbaar zijn op je toestel.',
'attributesToLog': 'Maken te loggen met cache attributen',
'attributesToLogDesc': 'Cache attributen worden ook geregistreerd als een eerste teken.',
'stripGC': '"GC" in GC-Code verwijderen',
'stripGCDesc': 'Oudere GPS toestellen kunnen problemen hebben met waypoints waarvan de naam langer is dan 8 tekens. Gebruik deze optie indien dit het geval is.',
'maxLogCount': 'Max aantal logs'
},
'theme': {
'select': 'Theme kiezen'
}
},
'autoTour': {
'wait': 'Even geduld – autoTour wordt aangemaakt!',
'radius': 'Radius',
'center': 'Middelpunt',
'refresh': 'Vernieuw voorbeeld',
'cacheCounts': 'Geocaches',
'duration': 'Duur',
'filterLabel': 'Filter',
'filter': {
'type': 'Type',
'size': 'Grootte',
'difficulty': 'Moeilijkheid',
'terrain': 'Terrein',
'special': {
'caption': 'Speciaal'
}
}
},
'dlg': {
'newVersion': {
'caption': 'Nieuwe versie beschikbaar',
'content': 'Er is een nieuwe versie van GCTour beschikbaar.\nWil je upgraden? \n\n'
},
'error': {
'content': '<img src="' + $.gctour.img.sad + '">&nbsp;&nbsp;Spijtig genoeg is er een fout gebeurd.<br/>' +
'Probeer het opnieuw proberen, of kijk voor <a href="#" id="gctour_update_error_dialog">update</a>!<br/><br/>'
}
},
'notifications': {
'addgeocache': {
'success': {
'caption': '{0} werd toegevoegd',
'content': '<b>{0}</b> bevat nu <b>{1}</b>.'
},
'contains': {
'caption': '{0} werd <i>niet</i> toegevoegd',
'content': '<b>{0}</b> bevat <b>{1}</b> reeds.'
}
}
},
'units': {
'km': 'Kilometer',
'mi': 'Miles'
},
'send2cgeo': {
'title': 'Naar c:geo versturen'
},
'marker': {
'coord': 'Coördinaten',
'content': 'Inhoud',
'contentHint': 'zal getoond worden in afdrukweergave'
},
'update': {
'dialog': '<div><p>Er is een nieuwe versie van <a target="_blank" href="' + GCTOUR_HOST + '"><b>GCTour</b></a> beschikbaar voor installatie.</p><p>Versie <b>###VERSION_OLD###</b> is momenteel geïnstalleerd, de recentste versie is <b>###VERSION_NEW###</b>.</p><div class="dialogFooter"></div>',
'upToDate': 'GCTour ' + VERSION + ' is op dit moment!'
},
'printview': {
'found': 'Gevonden',
'note': 'Note',
'marker': 'Eigen waypoint',
'removeMap': 'Kaart verwijderen',
'zoomMap': 'Open deze kaart in een nieuwe tab.',
'dontPrintHint': '<b>Ter info:</b><br/>Gegevens in dit kader worden <u>niet</u> afgedrukt!',
'print': 'Begin met afdrukken'
},
'cache': {
'addToTour': 'Aan toer toevoegen',
'directPrint': 'Deze geocache afdrukken',
'moveCoords': 'Verplaats de coördinaten',
'movedCoords': 'De coördinaten voor deze geocache zijn verplaatst.',
'moveCoordsHelp': 'Je kan de originele coördinaten van deze geocache verplaatsen. Deze worden dan gebruikt in de afdrukweergave en in het GPX bestand. Dit kan handig zijn bij een opgeloste mystery.',
'deleteCoords': 'Verwijderen coördinaten',
'originalCoords': 'Originele coördinaten',
'newCoords': 'Nieuwe coördinaten',
'addToCurrentTour': 'aan <b>huidige</b> toer',
'addToNewTour': 'aan <b>nieuwe</b> toer',
'shown': 'Getoonde geocaches',
'marked': 'Gemarkeerde geocaches',
'all': 'Alle caches'
},
'tour': {
'newDialog': 'Gelieve de naam van de nieuwe toer in te vullen ...',
'copy': 'Toer kopiëren',
'copyNameAppendix': 'Kopie',
'delete': 'Deze toer wissen',
'deleteDialog': 'Weet je zeker dat je deze toer wil verwijderen?',
'empty': 'De toer is leeg.'
}
};
$.gctour.i18n.pt = {
'name': 'Português',
'general': {
'save': 'Guardar',
'copy': 'copiar',
'cancel': 'Cancelar',
'close': 'Fechar',
'edit': 'Editar',
'rename': 'Renomear',
'delete': 'apagar',
'load': 'carregar',
'install': 'Instalar',
'findMe': 'Encontra-me!',
'exampleCoords': 'p.ex. N49° 26.082 E7° 46.587 ou 49.434701 7.776446',
'notLoggedIn': 'Você precisa estar logado, faça o login.',
'pleaseWait': 'Por favor aguarde - a carregar conte&#250;do ...'
},
'container': {
'toolbar': {
'newList': 'Nova Rota',
'openTour': 'Carregar uma Rota',
'downloadTour': {
'caption': 'Transferir Rota',
'webcodeDownloadHelp': 'Por favor introduza aqui o código que recebeu e clique em "Transferir Rota".',
'webcodeerror': 'O C&#243;digo escolhido n&#227;o existe!',
'webcodesuccess': ' foi carregada!',
'webcodeOld': '\n !!ATEN&#199;&#195;O!!\nEste c&#243;digo est&#225; conectado com uma rota antiga. Para obter todos os benef&#237;cios do GCTour 2.0, tem de enviar a rota novamente.'
},
'showSettings': 'Mostrar Configurações'
},
'tourHeader': {
'sendToGps': 'Enviar para o GPSr',
'downloadGpx': 'Transferir GPX',
'makeMap': {
'caption': 'Visualizar no mapa',
'wait': 'A testar disponibilidade deste mapa'
},
'upload': {
'caption': 'Enviar rota',
'tourUploaded1': '<br>Rota enviada com sucesso! C&#243;digo:<br><b>',
'tourUploaded2': '</b><br><br>Pode ver a rota em <a href="' + GCTOUR_HOST + 'tour/xWEBCODEx" target="_blank">' + GCTOUR_HOST + 'tour/xWEBCODEx</a>.<br><br>',
},
'addOwnWaypoint': 'Adicionar o seu Waypoint',
'sortAlphabetically': 'Ordenar alfabeticamente'
},
'cacheList': {
'logYourVisit': 'Registe a sua visita',
'removeFromList': 'Remover da lista'
}
},
'settings': {
'caption': 'Configurações',
'language': {
'header': 'Idioma',
'select': 'Escolha idioma'
},
'printview': {
'header': 'Modo de impressão',
'printMinimal': 'Modo de Impress&#227;o m&#237;nimo',
'printMinimalDesc': 'Cont&#233;m apenas a dica e o spoiler da geocache.',
'logCount': 'N&#250;mero de logs no modo de impress&#227;o',
'logCounts': ['nenhum', 'tudo', 'mostrar'],
'fontSize': 'Tamanho da letra',
'decryptHints': 'Decifrar dicas',
'decryptHintsDesc': 'As dicas v&#227;o estar decifradas no modo de impress&#227;o.',
'editDescription': 'Descri&#231;&#227;o edit&#225;vel',
'editDescriptionDesc': 'A descri&#231;&#227;o pode ser editada da forma como quiser.',
'showSpoiler': 'Mostrar spoiler',
'showSpoilerDesc': 'Imagens de spoiler v&#227;o estar no modo de impress&#227;o.',
'additionalWaypoints': 'Mostrar Waypoints Adicionais',
'additionalWaypointsDesc': 'O modo de impress&#227;o vai conter uma tabela com todos os "Waypoints Adicionais" de uma geocache.',
'loggedVisits': 'Mostrar contador de log',
'loggedVisitsDesc': 'Vai mostrar um resumo do "Contador de Visitas".',
'pageBreak': 'Espa&#231;o na pagina depois da cache',
'pageBreakDesc': 'Depois de cada geocache vai existir uma espa&#231;amento. Vis&#237;vel depois da impress&#227;o.',
'pageBreakAfterMap': 'Espa&#231;o na p&#225;gina depois do mapa',
'pageBreakAfterMapDesc': 'Vai existir um espa&#231;amento depois do resumo para separar as geocaches.',
'frontPage': 'Primeira p&#225;gina',
'frontPageDesc': 'Um resumo vai ser gerado incluindo uma lista completa de todas as geocaches com um &#237;ndice e um espa&#231;o para colocar notas.',
'outlineMap': 'Mapa com todas as caches',
'outlineMapDesc': 'O resumo vai conter um mapa com todas as geocaches.',
'outlineMapSingle': 'Mapa para todas as caches',
'outlineMapSingleDesc': 'Depois de cada geocache est&#225; um mapa contendo a geocache e os seus "Waypoints Adicionais".'
},
'map': {
'header': 'Mapa',
'type': 'Tipo de Mapa padr&#227;o',
'size': 'Tamanho de Mapa padr&#227;o',
'geocacheid': 'Mostrar id da Geocache',
'geocacheidDesc': 'O c&#243;digo GCCode (eg. GC0815) estar&#225; visivel no mapa.',
'geocacheindex': 'Mostrar ind&#237;ce da Geocache',
'geocacheindexDesc': 'A posi&#231;&#227;o de cada waypoint estar&#225; vis&#237;vel na rota seleccionada.',
'geocachename': 'Mostrar nome da Geocache',
'geocachenameDesc': 'O nome da geocache estar&#225; visivel.',
'awpts': 'Mostrar Waypoints Adicionais',
'awptsDesc': 'Se seleccionada, os Waypoints-Adicionais v&#227;o estar vis&#237;veis.',
'awpt_name': 'Mostrar o nome dos Waypoints Adicionais',
'awpt_nameDesc': 'O nome dos Waypoints-Adicionais v&#227;o estar no mapa.',
'awpt_lookup': 'Mostrar c&#243;digo dos Waypoints Adicionais',
'awpt_lookupDesc': 'Os c&#243;digos dos Waypoints-Adicionais v&#227;o estar vis&#237;veis.',
'owpts': 'Mostrar os nossos waypoints',
'owptsDesc': 'Se tem Waypoints seus na rota seleccionada e se esta op&#231;&#227;o estiver marcada, v&#227;o estar vis&#237;veis no mapa.',
'owpt_name': 'Mostrar o nome dos nossos waypoints',
'owpt_nameDesc': 'Mostrar o nome dos seus Waypoints'
},
'gpx': {
'html': 'Descri&#231;&#227;o com HTML',
'htmlDesc': 'Alguns programas/GPSr tem problemas em mostrar as geocaches se a descri&#231;&#227;o estiver no formato HTML. Se a descri&#231;&#227;o estiver confusa, desactive este op&#231;&#227;o.',
'wpts': 'Exportar waypoints adicionais',
'wptsDesc': 'Os waypoints-adicionais v&#227;o ser exportados como waypoint extra no GPX. Ir&#225; ver cada lugar de estacionamento na sua unidade.',
'attributesToLog': 'Criar login com atributos de cache',
'attributesToLogDesc': 'Atributos de cache também são registrados como um primeiro sinal.',
'stripGC': 'Remover "GC" no GC-Code',
'stripGCDesc': 'GPSr antigos tem problemas com os waypoints com nome maior que 8 caracteres. Use esta op&#231;&#227;o se tem uma unidade destas.',
'maxLogCount': 'Número máximo de logs'
},
'theme': {
'select': 'Escolha Theme'
}
},
'autoTour': {
'title': 'autoRota',
'wait': 'Por favor aguarde - criando a autoRota!',
'radius': 'Raio',
'center': 'Centro',
'refresh': 'Atualizar Visualização',
'cacheCounts': 'Geocaches',
'duration': 'Duração',
'filterLabel': 'Filtros',
'filter': {
'type': 'Typ',
'size': 'Rozmiar',
'difficulty': 'Trudność',
'terrain': 'Teren',
'special': {
'caption': 'Specjalne'
}
}
},
'dlg': {
'newVersion': {
'caption': 'Nova versão dispon&#xED;vel',
'content': 'Existe uma nova vers&#227;o de GCTour.\nDeseja actualizar? \n\n'
},
'error': {
'content': '<img src="' + $.gctour.img.sad + '">&nbsp;&nbsp;Lamento mas ocorreu um erro.<br/>' +
'Por favor tente outra vez, ou procure por uma <a href="#" id="gctour_update_error_dialog">atualização</a>!<br/><br/>'
}
},
'notifications': {
'addgeocache': {
'success': {
'caption': '{0} foi adicionada!',
'content': '<b>{0}</b> agora inclui <b>{1}</b>.'
},
'contains': {
'caption': '{0} não foi adicionada',
'content': '<b>{0}</b> contém <b>{1}</b> já.'
}
}
},
'units': {
'km': 'Quilometros',
'mi': 'Milhas'
},
'send2cgeo': {
'title': 'Enviar para o c:geo'
},
'marker': {
'coord': 'Coordenadas',
'content': 'Conte&#250;do',
'contentHint': 'estar&#225; visivel no modo de impress&#227;o',
'type': 'Tipo'
},
'update': {
'dialog': '<div><p>Existe uma nova vers&#227;o de <a target="_blank" href="' + GCTOUR_HOST + '"><b>GCTour</b></a> dispon&#237;vel para instalar.</p><p>Tem a versão <b>###VERSION_OLD###</b> instalada, a &#250;ltima versão &#233; <b>###VERSION_NEW###</b>.</p><div class="dialogFooter"></div>',
'upToDate': 'GCTour ' + VERSION + ' está atualmente!'
},
'printview': {
'found': 'Encontrada',
'note': 'Nota',
'marker': 'Waypoint pr&#243;prio',
'removeMap': 'Remover mapa',
'zoomMap': 'Abrir este mapa num novo separador.',
'dontPrintHint': '<b>Informa&#231;&#227;o:</b><br />Elementos na caixa <u>n&#227;o</u> v&#227;o ser impressos!',
'print': 'Iniciar a impressão'
},
'cache': {
'addToTour': 'Adicionar à rota',
'directPrint': 'Imprimir esta Geocache',
'moveCoords': 'Mover as coordenadas',
'movedCoords': 'As coordenadas desta geocache foram mudadas.',
'moveCoordsHelp': 'Tens a oportunidade de mudar as coordenadas originais desta geocache. Ser&#227;o usadas no modo de impress&#227;o e tamb&#233;m no ficheiro GPX. &#201; util se resolver o enigma.',
'deleteCoords': 'Coordenadas para deletar',
'originalCoords': 'Coordenadas Originais',
'newCoords': 'Novas Coordenadas',
'addToCurrentTour': 'para a rota <b>seleccionada</b>',
'addToNewTour': 'para uma <b>nova</b> rota',
'shown': 'Geocaches vis&#237;veis',
'marked': 'Geocaches marcados',
'all': 'Tudo Caches'
},
'tour': {
'newDialog': 'Introduza um nome para a nova rota ...',
'copy': 'Copiar rota',
'copyNameAppendix': 'Cópia',
'delete': 'Apagar esta rota',
'deleteDialog': 'Deseja mesmo remover esta rota?',
'empty': 'A lista est&#225; vazia.'
}
};
// init translations
(function() {
/*$.each($.gctour.i18n, function(l, o) {
log_table(o);
});*/
$.gctour.lang = function(str) {
var i18n = $.gctour.i18n,
cur = $.gctour.currentLang,
def = $.gctour.defaultLang,
i,
arr,
trans,
cur_trans,
def_trans;
// try to get current and default translation
cur_trans = i18n[cur] || false;
def_trans = i18n[def] || false;
arr = str.split('.');
for (i = 0; i < arr.length; i++) {
cur_trans = cur_trans ? cur_trans[arr[i]] : undefined;
def_trans = def_trans ? def_trans[arr[i]] : undefined;
}
// use default language as fallback
trans = (cur_trans !== undefined) ? cur_trans :
(def_trans !== undefined) ? def_trans :
((DEBUG_MODE === true) ? "MISSING TRANSLATION" : "");
/*
// debug info current language
if (!i18n[cur]) {
debug("ERROR: language '" + cur + "' is undefined");
} else if (cur_trans === undefined) {
debug("ERROR: active language (" + cur + "), search '" + str + "' is undefined");
}
// debug info default language
if (!i18n[def]) {
debug("ERROR: language '" + def + "' is undefined");
} else if (def_trans === undefined) {
debug("ERROR: default language (" + def + "), search '" + str + "' is undefined");
}
*/
return trans;
};
})();
/* styles: CSS Container, run before init() */
function initStyle() {
// adding styles
GM_addStyle(("" +
"/* GCTour - Container */\n" +
"" +
"#gctourButtonWrapper {" +
" height: 32px !important;" +
" padding: 0 !important;" +
" position: fixed !important;" +
" top: 30px !important;" +
" width: 35px !important;" +
" background-color: #fff;" +
" z-index: 10000 !important;" +
" border: 1px solid #333;" +
" border-width: 1px 1px 1px 0;" +
" border-radius: 0 5px 5px 0;" +
" -moz-user-select: none;" +
"}" +
"" +
"#gctourButtonWrapper img {" +
" position: relative;" +
" top: 8px;" +
" left: 8px;" +
" vertical-align: top;" +
"}" +
"" +
"#gctourContainer {" +
" background-color: #fff;" +
" overflow: hidden;" +
" left: -210px;" +
" padding: 0 !important;" +
" position: fixed !important;" +
" top: 15px !important;" +
" width: 200px;" +
" z-index: 10001 !important;" +
" border: 1px solid #333;" +
" border-left: 0px;" +
" border-radius: 0 5px 5px 0;" +
" font-size: 12px;" +
" font-family: Arial;" +
" line-height: 1.5;" +
"}" +
"" +
"#gctourContainer .cachelist {" +
" width: 100%;" +
" margin: 0;" +
" padding:0;" +
" font-size:80%;" +
" list-style-type:none;" +
"}" +
"" +
"#gctourContainer .cachelist li {" +
" color:#000;" +
" margin:0.5em;" +
" padding:3px;" +
" width:120px;" +
" min-height:44px;" +
" list-style-position:inside;" +
" border:1pt dashed gray;" +
" background-color:#FFF;" +
" -moz-background-clip:border;" +
" -moz-background-inline-policy:continuous;" +
" -moz-background-origin:padding;" +
" -moz-border-radius:8px 0 8px 0;" +
" border-radius:8px 0 8px 0;" +
" box-sizing:content-box;" +
"}" +
"" +
"#gctourContainer img.imgShadow {" +
" -moz-box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.2);" +
" box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.2);" +
" background-color: lightgray;" +
"}" +
"" +
"#gctourContainer img.tourImage {" +
" cursor: pointer;" +
" margin: 0 2px 0 2px;" +
"}" +
"" +
"/* Styling the placeholder for when the user starts dragging an item */\n" +
"" +
"#gctourContainer li.ui-sortable-placeholder {" +
" min-height:50px;" +
" max-height:80px;" +
" width: 90%;" +
" background-color: rgba(0, 0, 0, 0.03);" +
"}" +
"/* GCTour - Grand */\n" +
"" +
".gctour-grand-default {" +
" /* http://www.colorzilla.com/gradient-editor/#a7cfef+0,c9dded+3,ffffff+10;gctour-grand-default" +
" * http://css3please.com/" +
" */" +
" background: rgb(167,207,239); /* Old browsers */" +
" background: -moz-linear-gradient(top, rgba(167,207,239,1) 0%, rgba(201,221,237,1) 3px, rgba(255,255,255,1) 10px); /* FF3.6+ */" +
" background: -webkit-linear-gradient(top, rgba(167,207,239,1) 0%,rgba(201,221,237,1) 3px,rgba(255,255,255,1) 10px); /* Chrome10+,Safari5.1+ */" +
" background: -o-linear-gradient(top, rgba(167,207,239,1) 0%,rgba(201,221,237,1) 3px,rgba(255,255,255,1) 10px); /* Opera 11.10+ */" +
" background: linear-gradient(top, rgba(167,207,239,1) 0%,rgba(201,221,237,1) 3px,rgba(255,255,255,1) 10px); /* W3C */" +
"}" +
"" +
".gctour-grand-hover {" +
" /* http://www.colorzilla.com/gradient-editor/#ffad32+0,ffd699+3,ffffff+10;gctour-grand-hover */" +
" background: rgb(255,173,50); /* Old browsers */" +
" background: -moz-linear-gradient(top, rgba(255,173,50,1) 0%, rgba(255,214,153,1) 3px, rgba(255,255,255,1) 10px); /* FF3.6+ */" +
" background: -webkit-linear-gradient(top, rgba(255,173,50,1) 0%,rgba(255,214,153,1) 3px,rgba(255,255,255,1) 10px); /* Chrome10+,Safari5.1+ */" +
" background: -o-linear-gradient(top, rgba(255,173,50,1) 0%,rgba(255,214,153,1) 3px,rgba(255,255,255,1) 10px); /* Opera 11.10+ */" +
" background: linear-gradient(top, rgba(255,173,50,1) 0%,rgba(255,214,153,1) 3px,rgba(255,255,255,1) 10px); /* W3C */" +
"}" +
"" +
".gctour-grand-highlight {" +
" /* http://www.colorzilla.com/gradient-editor/#ffe000+0,ffee7f+3,ffffff+10;gctour-grand-highlight */" +
" background: rgb(255,224,0); /* Old browsers */" +
" background: -moz-linear-gradient(top, rgba(255,224,0,1) 0%, rgba(255,238,127,1) 3px, rgba(255,255,255,1) 10px); /* FF3.6+ */" +
" background: -webkit-linear-gradient(top, rgba(255,224,0,1) 0%,rgba(255,238,127,1) 3px,rgba(255,255,255,1) 10px); /* Chrome10+,Safari5.1+ */" +
" background: -o-linear-gradient(top, rgba(255,224,0,1) 0%,rgba(255,238,127,1) 3px,rgba(255,255,255,1) 10px); /* Opera 11.10+ */" +
" background: linear-gradient(top, rgba(255,224,0,1) 0%,rgba(255,238,127,1) 3px,rgba(255,255,255,1) 10px); /* W3C */" +
"}" +
"" +
".gctour-grand-active {" +
" /* Grün http://www.colorzilla.com/gradient-editor/#3dff32+0,9eff99+3,ffffff+10;gctour-grand-active */" +
" background: rgb(61,255,50); /* Old browsers */" +
" background: -moz-linear-gradient(top, rgba(61,255,50,1) 0%, rgba(158,255,153,1) 3px, rgba(255,255,255,1) 10px); /* FF3.6+ */" +
" background: -webkit-linear-gradient(top, rgba(61,255,50,1) 0%,rgba(158,255,153,1) 3px,rgba(255,255,255,1) 10px); /* Chrome10+,Safari5.1+ */" +
" background: linear-gradient(top, rgba(61,255,50,1) 0%,rgba(158,255,153,1) 3px,rgba(255,255,255,1) 10px); /* W3C */$$$$" +
" background: -o-linear-gradient(top, rgba(61,255,50,1) 0%,rgba(158,255,153,1) 3px,rgba(255,255,255,1) 10px); /* Opera 11.10+ */" +
"}" +
"" +
".gctour-grand-error {" +
" /* http://www.colorzilla.com/gradient-editor/#ff3232+0,ff9999+3,ffffff+10;gctour-grand-error */" +
" background: rgb(255,50,50); /* Old browsers */" +
" background: -moz-linear-gradient(top, rgba(255,50,50,1) 0%, rgba(255,153,153,1) 3px, rgba(255,255,255,1) 10px); /* FF3.6+ */" +
" background: -webkit-linear-gradient(top, rgba(255,50,50,1) 0%,rgba(255,153,153,1) 3px,rgba(255,255,255,1) 10px); /* Chrome10+,Safari5.1+ */" +
" background: -o-linear-gradient(top, rgba(255,50,50,1) 0%,rgba(255,153,153,1) 3px,rgba(255,255,255,1) 10px); /* Opera 11.10+ */" +
" background: linear-gradient(top, rgba(255,50,50,1) 0%,rgba(255,153,153,1) 3px,rgba(255,255,255,1) 10px); /* W3C */" +
"}" +
"" +
"/* GCTour Slider */\n" +
"" +
".gct_scrollbar {" +
" background-color:pink;" +
" height: 20px;" +
" left: 0;" +
" position: absolute;" +
" top: 0;" +
" width: 100%;" +
"}" +
"" +
".gct_scroller{" +
" background-color:lime;" +
" height: 20px;" +
" left: 0;" +
" position: absolute;" +
" top: 0;" +
" width: 20px; " +
"}" +
"" +
"/* GCTour Pop-Up */\n" +
"" +
".gct_popup{" +
" position:absolute;" +
" z-index:10; " +
" width:172px;" +
" height:102px;" +
" text-align:center;" +
" color:#FF0000;" +
" font: 14px Verdana, Arial, Helvetica, sans-serif;" +
" background-color:lime;" +
" border-radius: 5px;" +
"}" +
"" +
".gct_popup_header{" +
" border-radius: 5px 5px 0px 0px;" +
"}" +
"" +
".dialogMask {" +
" background-image:url(##dialogMaskImage##);" +
" height:100%;" +
" left:0;" +
" opacity:0.7;" +
" position:fixed;" +
" top:0;" +
" width:100%;" +
" z-index:1100;" +
"}" +
"" +
".dialogBody {" +
" -moz-border-radius:5px;" +
" border-radius:5px;" +
" background:none repeat scroll 0 0 #fff;" +
" border:1px solid #333333;" +
" color:#333333;" +
" cursor:default;" +
" font-family:Arial;" +
" font-size:12px;" +
" left:50%;" +
" margin-left:-250px;" +
" margin-top:20px;" +
" padding:0 0 1em;" +
" position:fixed;" +
" text-align:left;" +
" top:0;" +
" width:625px;" +
" z-index:9999;" +
" max-height:85%;" +
" min-height:440px;" +
" overflow:auto;" +
"}" +
"" +
".dialogBody p {" +
" font-size:12px;" +
" font-weight:normal;" +
" margin:1em 0;" +
"}" +
"" +
".dialogBody button {" +
" background: none no-repeat scroll 4px center #eeeeee;" +
" border: 1px outset #666666" +
"}" +
"" +
".dialogBody button:hover {" +
" background-color: #f9f9f9;" +
"}" +
"" +
"/* autoTour */\n" +
"" +
".dialogBody select {" +
" width: auto;" +
" display: initial;" +
" padding: initial;" +
" -moz-appearance: menulist;" +
" -webkit-appearance: menulist;" +
"}" +
"" +
".dialogBody label {" +
" text-transform: none;" +
" display: inline;" +
"}" +
"" +
".header h1 {" +
" background-color:#B2D4F3;" +
" background-repeat:repeat-x;" +
" font-size:110% !important;" +
" font-family:Helvetica Neue,Arial,Helvetica,sans-serif;" +
" margin-bottom:0.2em;" +
" margin-top:0;" +
" padding:0.5em;" +
" -moz-border-radius: 5px;" +
" border-radius: 5px;" +
" color:#333333;" +
" background-image:url(##tabBgImage##)" +
"}" +
"" +
"/*" +
".dialogBody h1 {" +
" background-color:#7A7A7A;" +
" border-bottom:1px solid #333333;" +
" font-size:110%;" +
" font-family:Helvetica Neue,Arial,Helvetica,sans-serif;" +
" margin-bottom:0.2em;" +
" padding:0.5em;" +
" -moz-border-radius:5px;" +
" border-radius:5px;" +
" color:#fff;" +
"}" +
"*/" +
"" +
".dialogHistory {" +
" border:1px inset #999999;" +
" margin:0 1em 1em;" +
" padding-bottom: 1em;" +
" max-height: 200px;" +
" overflow-y:auto;" +
" width:518px;" +
" padding-left:1em;" +
"}" +
"" +
".dialogHistory ul {" +
" margin-left:2em;" +
"}" +
"" +
".dialogHistory li {" +
" list-style-type:circle;" +
"}" +
"" +
".dialogFooter input {" +
" -moz-border-radius:3px;" +
" border-radius:3px;" +
" background:none no-repeat scroll 4px center #EEEEEE;" +
" border:1px outset #666666;" +
" cursor:pointer;" +
" float:right;" +
" margin-left:0.5em;" +
" padding:3px 5px 5px 20px;" +
" min-width:100px;" +
" font-size: 12px;" +
"}" +
"" +
".dialogFooter input:hover {" +
" background-color:#f9f9f9;" +
"}" +
"" +
".dialogContent {" +
" padding:0 10px 0 10px;" +
"}" +
"" +
".dialogMin {" +
" min-height:0 !important" +
"}" +
"" +
"" +
"/* neuer Dialog-Style mit jQuery-ui + gcTour Header (.gct_dialog) */\n" +
"" +
".gct_dialog {" +
" font-size: 100% !important;" +
" font-family: Arial !important;" +
" z-index: 9999 !important;" +
"}" +
".gct_dialog .ui-widget {" +
"}" +
".gct_dialog.ui-dialog {" +
" padding: 0;" +
"}" +
".gct_dialog.ui-dialog .ui-widget-header {" +
" border: 0;" +
"}" +
".gct_dialog.ui-dialog .ui-dialog-titlebar {" +
" padding: 0.2em 0.1em;" +
//" background: -moz-linear-gradient(center top, #A7CFEF 0%, #C9DDED 3px, #FFFFFF 10px) repeat scroll 0 0 transparent;" +
" color: #000;" +
"}" +
".gct_dialog.ui-dialog .ui-dialog-title {" +
" padding-top: 4px;" +
" text-align: left;" +
" padding-left: 120px;" +
" width: 75%;" +
" background: url(##gctourLogo##) 2px 2px no-repeat;" +
"}" +
".gct_dialog.ui-dialog .ui-dialog-buttonpane {" +
" padding: 0 0.4em 0 0.4em;" +
"}" +
".gct_dialog .ui-button-text-only .ui-button-text {" +
" padding: 0.2em 0.8em;" +
"}" +
".gct_dialog .ui-progressbar .ui-progressbar-value {" +
" margin: 0;" +
"}" +
".gct_dialog .progressbar-label {" +
" width: 80%;" +
" text-align: center;" +
"}" +
"" +
"/* jqui settings dialog */\n" +
"" +
"div.gct_dialog select {" +
" width: auto;" +
" display: initial;" +
" padding: initial;" +
" -moz-appearance: menulist;" +
" -webkit-appearance: menulist;" +
"}" +
"div.gct_dialog label {" +
" text-transform: none;" +
" font-size: 100%;" +
"}" +
"div.gct_dialog input[type='radio'] {" +
" display: initial;" +
"}" +
"" +
"#dialogDetails {" +
" height:364px;" +
" padding:3px;" +
" overflow:auto;" +
" background-color:#eff4f9;" +
" border:1px solid #C0CEE3;" +
" -moz-border-radius: 0 5px 5px 0;" +
" border-radius: 0 5px 5px 0;" +
" width:424px;" +
" position: absolute;" +
" right: 10px;" +
"}" +
"" +
".dialogList {" +
" margin:0;" +
" padding:0;" +
"}" +
".dialogList li {" +
" font-size:10px;" +
" padding:3px;" +
" clear:both;" +
" list-style-type: none;" +
"}" +
"" +
".activeTour, .gct_sortable-placeholder {" +
" border: 1px solid #C0CEE3;" +
" -moz-border-radius: 5px 0 0 5px;" +
" border-radius: 5px 0 0 5px;" +
" background-color: #eff4f9;" +
" padding: 1px;" +
"}" +
".gct_sortable-placeholder {" +
" background-color: rgba(0, 0, 0, 0.03);" +
" height: 3em;" +
"}" +
"" +
"#dialogListContainer {" +
" height:374px;" +
" overflow:auto;" +
" width:146px;" +
" position: absolute;" +
" left: 10px;" +
"}" +
"" +
"#dialogListContainer a{" +
" text-decoration:underline;" +
"}" +
"" +
".unselectable {" +
" -o-user-select: none;" +
" -webkit-user-select: none;" +
" -moz-user-select: -none;" +
" -khtml-user-select: none;" +
" user-select: none;" +
"}" +
"" +
"#cacheList .counter {" +
" position:absolute;" +
" right:4px;" +
" bottom: 0;" +
" z-index:0;" +
" overflow:hidden;" +
" font: normal 24px arial,sans-serif;" +
" color: #d5d5d5;" +
" text-align:right;" +
" text-shadow: 1px 1px 1px #C0C0C0;" +
" vertical-align: text-bottom;" +
" background-color: transparent;" +
" margin-right:0;" +
" margin-bottom:0;" +
" padding: 0;" +
"}" +
"" +
"#gctour-notification-box {" +
" position: fixed;" +
" right: 4px;" +
" bottom: 2%;" +
" width: 220px;" +
" height: auto;" +
" max-height: 96%;" +
" overflow: hidden;" +
" overflow-y: auto;" +
" list-style-type: none;" +
" margin: 0;" +
" padding: 0;" +
" z-index: 1102;" +
"}" +
"" +
"#gctour-notification-box li {" +
" overflow: hidden;" +
" background-image: url('" + $.gctour.img.bg + "');" +
" background-repeat: repeat-x;" +
" background-attachment: scroll;" +
" background-position: left top;" +
" box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5);" +
" width: 220px;" +
" cursor: pointer;" +
"}" +
"" +
".gctour-notification-green {" +
" background-color: lightgreen;" +
" color: #000000;" +
" border: 1px solid #50C24E;" +
" text-shadow: 0 1px 0 #FFFFFF;" +
"}" +
"" +
".gctour-notification-red {" +
" background-color: red;" +
" border: 1px solid #8B0000;" +
" color: #FFFFFF;" +
" text-shadow: 0 1px 0 #000000;" +
"}" +
"" +
".gctour-notification-blue {" +
" background-color: #57B7E2;" +
" border: 1px solid #0B90C4;" +
" color: #000000;" +
" text-shadow: 0 1px 0 #FFFFFF;" +
"}" +
"" +
".gctour-notification-yellow {" +
" background-color: #FFFC00;" +
" border: 1px solid #FFC237;" +
" color: #000000;" +
" text-shadow: 0 1px 0 #FFFFFF;" +
"}" +
"" +
"/* jquery ui overwrite */\n" +
"" +
".ui-button-icon-only .ui-icon {" +
" margin-top: -8px !important;" +
" margin-left: -8px !important;" +
"}" +
"" +
".gct .ui-widget-content {" +
" background: #FFFFFF;" +
" /* border: 0; */" +
"}" +
"" +
".gct .ui-widget-content a {" +
" color: #00447c;" +
" text-decoration: underline;" +
"}" +
"" +
".ui-button-text-only .ui-button-text {" +
" padding: .2em .6em;" +
"}" +
"" +
"input.ui-button {" +
" padding: .2em .6em;" +
"}" +
"" +
".ui-dialog .ui-dialog-buttonpane {" +
" margin-top: 0;" +
"}" +
"" +
"/* Handling of Groundspeak's stylesheets: different sites use different stylesheets */\n" +
"" +
".gctour-input-checkbox {" +
" position: relative !important;" +
" opacity: 1 !important;" +
" width: 14px !important;" +
" height: 14px !important;" +
"}" +
"" +
".gctour-input-text {" +
" padding: unset !important;" +
" font-size: 1em !important;" +
" font-family: inherit !important;" +
" border: 1px solid #ccc !important;" +
" border-radius: 3px !important;" +
" height: auto !important;" +
" line-height: inherit !important;" +
"}" +
"" +
".gctour-label {" +
" display: unset !important;" +
"}" +
"" +
".gctour-small {" +
" font-size: 0.6875rem !important;" +
"}" +
"" +
"/* send2cgeo */\n" +
"" +
"#gctour_send2cgeo_progressbar {" +
" margin: 2px 0;" +
"}" +
"" +
".hide {" +
" display: none;" +
"}" +
".ui-progressbar {" +
" position: relative;" +
" width: 60%;" +
"}" +
"" +
".ui-progressbar-value {" +
"}" +
".progress-label {" +
" position: absolute;" +
" width: 100%;" +
" text-align: center;" +
" top: 4px;" +
" font-weight: bold;" +
"}" +
"div.gct_send2cgeo ol li {" +
" padding: 2px;" +
"}" +
"div.gct_send2cgeo ul {" +
" list-style-type: disc;" +
"}" +
"div.gct_send2cgeo ul, ol {" +
" padding-left: 1.5em;" +
" margin-bottom: 0.5em;" +
" margin-left: 1.5em;" +
"}" +
"div.gct_send2cgeo a {" +
" text-decoration: underline;" +
"}" +
"")
.replace("##gctourLogo##", $.gctour.img.gctourLogo)
.replace("##dialogMaskImage##", $.gctour.img.dialogMask)
.replace("##tabBgImage##", $.gctour.img.tabBg));
}
/* utilities */
// Read all GET URL variables and return them as an associative array.
function getUrlVars(url) {
var vars = [],
hash;
var hashes = url.slice(url.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
function createElement(type, attributes) {
var node = document.createElement(type);
for (const attr in attributes) {
if (Object.hasOwn(attributes, attr)) {
node.setAttribute(attr, attributes[attr]);
}
}
return node;
}
function append(thisElement, toThis) {
return toThis.appendChild(thisElement);
}
function fillTemplate(mapping, template) {
var j,
dummy;
for (j = 0; j < mapping.length; j++) {
template = template.replaceAll("###" + mapping[j][0] + "###", mapping[j][1]);
}
dummy = createElement('div');
dummy.innerHTML = template;
return dummy.firstChild;
}
// rot13.js from gc.com
function createROT13array() {
var A,
C = [],
D = "abcdefghijklmnopqrstuvwxyz",
B = D.length;
for (A = 0; A < B; A++) {
C[D.charAt(A)] = D.charAt((A + 13) % 26);
}
for (A = 0; A < B; A++) {
C[D.charAt(A).toUpperCase()] = D.charAt((A + 13) % 26).toUpperCase();
}
return C;
}
function convertROT13Char(A) {
return (A >= "A" && A <= "Z" || A >= "a" && A <= "z" ? ROT13_ARRAY[A] : A);
}
function convertROT13String(C) {
var A,
B = C.length,
D = "";
if (!ROT13_ARRAY) {
ROT13_ARRAY = createROT13array();
}
for (A = 0; A < B; A++) {
D += convertROT13Char(C.charAt(A));
}
return D;
}
function convertROTStringWithBrackets(C) {
var F = "",
D = "",
E = true,
A,
B = C.length;
if (!ROT13_ARRAY) {
ROT13_ARRAY = createROT13array();
}
for (A = 0; A < B; A++) {
F = C.charAt(A);
if (A < (B - 4)) {
if (C.toLowerCase().substr(A, 4) == "<br/>") {
D += "<br/>";
A += 3;
continue;
}
}
if (F == "[" || F == "<") {
E = false;
} else {
if (F == "]" || F == ">") {
E = true;
} else {
if ((F === " ") || (F === "&dhbg;")) {}
else {
if (E) {
F = convertROT13Char(F);
}
}
}
}
D += F;
}
return D;
}
// Replace all &,< and > with their HTML tag; additionally replace <br> by <br /> (may cause problems on Garmin units)
function escapeHTML(htmlString) {
return (!htmlString) ? "" : htmlString.replace(/<br[^>]*>/gi, '<br />').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
// strip all ASCII control codes (range 0-31) from a string, except LF and CR
function stripASCIIcodes(s) {
return s.split('').filter(function(x) {
var n = x.charCodeAt(0);
return n==10 || n==13 || n>31; // keep LF and CR
}).join('');
}
function xsdDateTime(date) {
function pad(n) {
var s = n.toString();
return (s.length < 2) ? '0' + s : s;
}
var yyyy = date.getFullYear(),
mm1 = pad(date.getMonth() + 1),
dd = pad(date.getDate()),
hh = pad(date.getHours()),
mm2 = pad(date.getMinutes()),
ss = pad(date.getSeconds());
return yyyy + '-' + mm1 + '-' + dd + 'T' + hh + ':' + mm2 + ':' + ss + 'Z';
}
// GET, POST, PUT, DELETE
function promiseRequest(method, url, headers, data, progressbar=false) {
debug(["---PROMISEREQUEST---",
"\tmethod: " + method,
"\turl: " + url,
"\theaders: " + JSON.stringify(headers),
"\tdata: " + data,
"---PROMISEREQUEST---"
].join("\n"));
// return a promise
return new Promise(function(resolve, reject) {
GM_xmlhttpRequest({
method : method,
url : url,
headers : headers,
data : data,
onload : function(result) {
if (progressbar) updateProgressBar();
responseInfo(result);
if (result.status >= 200 && result.status < 300) { // ok
resolve(result);
} else { // error
//reject(' error: ' + result.status + ', message: ' + result.statusText + ', error message: ' + errMess);
reject(result.responseText);
}
},
ontimeout : function() {
let l = new URL(url);
reject(' timeout detected: "no answer from ' + l.host + ' for ' + timeout / 1000 + 's"');
},
onerror : function(result) { // network error
responseInfo(result);
reject(' error: ' + result.status + ', message: ' + result.statusText);
}
});
});
}
// Run a function as soon as a specific DOM element is available
function waitForDOMelementThenRun(selector, func, timeout) {
waitForConditionThenRun('document.querySelector(\''+selector+'\')', func, timeout);
}
// Run a function as soon as a specific condition is fulfilled
// Condition input is a string and can be anything to be checked by an if condition:
// - DOM element exists: str = 'document.querySelector("body")'
// - object exists and is non-empty: str = 'unsafeWindow.object.length > 0'
// - variable exists: str = 'variable'
function waitForConditionThenRun(str, func, timeout=30000) {
// fine, condition is already fulfilled, run func immediately
try {
if (eval(str)) {
func();
return;
}
} catch {}
// otherwise, observe html until condition is fulfilled, or timeout
const t0 = Date.now();
const observer = new MutationObserver(() => {
// Timeout.
if (Date.now() - t0 > timeout) {
observer.disconnect();
log('waitForConditionThenRun() - ' + str + ' timed out after ' + timeout/1000 + 's');
}
try {
if (eval(str)) {
observer.disconnect();
func();
log('waitForConditionThenRun() - ' + str + ' took ' + (Date.now() - t0)/1000 + 's');
}
} catch {}
});
observer.observe(document.documentElement, { childList: true, subtree: true });
}
/* dates */
function DateDiff(date1, date2, einheit) {
var ms = date1.getTime() - date2.getTime(); // milliseconds
var diff;
switch (einheit) {
case "second":
diff = ms / 1000;
break;
case "minute":
diff = ms / 60000; // 1000 * 60
break;
case "hour":
diff = ms / 3600000; // 1000 * 60 * 60
break;
case "day":
diff = ms / 86400000; // 1000 * 60 * 60 * 24
break;
case "week":
diff = ms / 604800000; // 1000 * 60 * 60 * 24 * 7
break;
default:
diff = ms;
break;
}
return diff;
}
function dateFormatConversion(format) {
return format.replace(/yy/g,'y').replace(/M/g,'m').replace(/mmm/,'M');
/* GS dateformat to jqui datepicker dateformat:
https://www.geocaching.com/account/settings/preferences#SelectedDateFormat
http://api.jqueryui.com/datepicker/#utility-parseDate
GS --> jqui: d-->d,dd-->dd,M-->m,MM-->mm,MMM-->M,yy-->y,yyyy-->yy
"d. M. yyyy" : "d. m. yy", 3. 1. 2017
"d.M.yyyy" : "d.m.yy", 3.1.2017
"d.MM.yyyy" : "d.mm.yy", 3.01.2017
"d/M/yy" : "d/m/y", 3/1/17
"d/M/yyyy" : "d/m/yy", 3/1/2017
"d/MM/yyyy" : "d/mm/yy", 3/01/2017
"dd MMM yy" : "dd M y", 03 Jan 17
"dd.MM.yy" : "dd.mm.y", 03.01.17
"dd.MM.yyyy" : "dd.mm.yy", 03.01.2017
"dd.MM.yyyy." : "dd.mm.yy.", 03.01.2017.
"dd.MMM.yyyy" : "dd.M.yy", 03.Jan.2017
"dd/MM/yy" : "dd/mm/y", 03/01/17
"dd/MM/yyyy" : "dd/mm/yy", 03/01/2017
"dd/MMM/yyyy" : "dd/M/yy", 03/Jan/2017
"dd-MM-yy" : "dd-mm-y", 03-01-17
"dd-MM-yyyy" : "dd-mm-yy", 03-01-2017
"d-M-yyyy" : "d-m-yy", 3-1-2017
"M/d/yyyy" : "m/d/yy", 1/3/2017
"MM/dd/yyyy" : "mm/dd/yy", 01/03/2017
"MMM/dd/yyyy" : "M/dd/yy", Jan/03/2017
"yyyy.MM.dd." : "yy.mm.dd.", 2017.01.03.
"yyyy/MM/dd" : "yy/mm/dd", 2017/01/03
"yyyy-MM-dd" : "yy-mm-dd" 2017-01-03 */
}
async function parseDate(date_string) {
var gs_date_format = GS_USERINFO.dateFormat,
jqui_date_format = dateFormatConversion(gs_date_format),
date,
debugStr = "date to parse: '" + date_string + "'\nGS format: '" + gs_date_format + "'\njqui format: '" + jqui_date_format;
try {
date = $.datepicker.parseDate(jqui_date_format, date_string);
debug(debugStr + "'\nParsed date: '" + date + "'");
} catch (e) {
var err = 'Date format mismatch in fn parseDate - "' + e + '"<br>&nbsp;&nbsp;date format: ' + gs_date_format + '<br>&nbsp;&nbsp;date to parse: ' + date_string;
if (gs_date_format === 'undefined') { // most probably third-party cookies are not accepted by Firefox
err += '<br><br><u>Bitte Firefox-Einstellungen prüfen: Cookies von Drittanbietern müssen akzeptiert werden.</u>';
err += '<br><u>Please check Firefox settings: third-party cookies need to be accepted.</u>';
}
throw err;
}
return date;
}
async function formatDate(date) {
var gs_date_format = GS_USERINFO.dateFormat,
jqui_date_format = dateFormatConversion(gs_date_format);
try {
var date_string = $.datepicker.formatDate(jqui_date_format, date);
debug("date to format: '" + date + "'\nGS format: '" + gs_date_format + "'\njqui format: '" + jqui_date_format + "'\nDatestring: '" + date_string + "'");
} catch(e) {
throw 'Date format mismatch in fn formatDate - "' + e + '"<br>&nbsp;&nbsp;date to format: ' + date + '<br>&nbsp;&nbsp;GS format: ' + gs_date_format;
}
return date_string;
}
/* geo utilities */
/** Orientiert an Geodesy representation conversion functions (c) Chris Veness 2002-2011 **/
var Geo = {}; // Geo namespace, representing static class
/**
* Interpretiert einen String als Gradzahl. Diese Funktion verarbeitet alle 3 möglichen Formate (d, dm, dms)
* Limitiert auf eine Komponente pro Aufruf.
*
* @param {String} dmsStr: Koordinaten String
* @returns {Number} deg: Degrees
*/
Geo.parseDMS = function(dmsStr) {
// entferne alle nicht Zahlen (Regex:[^\d.\s]) und teile den String an den verbleibenden Leerzeichen (Regex:[^0-9.,])
var dms = dmsStr.replace(/[^\d.\s]/g, ' ').trim().split(/[^0-9.,]+/);
var deg;
// wenn nix mehr übrig bleibt -> keine Koordinate
if (dms == '') {
return NaN;
}
// Anhand der Länge von dms wird ermittelt im welchem Format die Koordinaten vorliegen
switch (dms.length) {
case 3: // interpret 3-part result as d/m/s
deg = dms[0] / 1 + dms[1] / 60 + dms[2] / 3600;
break;
case 2: // interpret 2-part result as d/m
deg = dms[0] / 1 + dms[1] / 60;
break;
case 1: // just d (possibly decimal) or non-separated dddmmss
deg = dms[0];
break;
default:
return NaN;
}
// anschließend negiere Wert wenn der String ein S oder W beinhaltet
if (/^-|^[WS]/i.test(dmsStr.trim())) {
deg = -deg;
}
return deg;
};
/**
* Konvertiert dezimal Gradzahlen zu dem festgelgegten Format ('d', 'dm', 'dms') - Vorangestellt N/S
*
* @param {Number} deg: Degrees
* @param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
* @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
* @returns {String} Deg/min/seconds
*/
Geo.toLat = function(deg, format) {
var lat = Geo.toDMS(deg, format);
return lat == '' ? '' : (deg < 0 ? 'S' : 'N') + " " + lat.slice(1); // erste '0' abschneiden für Lat
};
/**
* Konvertiert dezimal Gradzahlen zu dem festgelgegten Format ('d', 'dm', 'dms') - Vorangestellt E/W
*
* @param {Number} deg: Degrees
* @param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
* @returns {String} Deg/min/seconds
*/
Geo.toLon = function(deg, format) {
var lon = Geo.toDMS(deg, format);
return lon == '' ? '' : (deg < 0 ? 'W' : 'E') + " " + lon;
};
/**
* Konvertiert dezimal Gradzahlen in das "deg°"(d), "deg° min" (dm) oder "deg°min'sec''"(dms) Format
*
* @private
* @param {Number} deg: Degrees
* @param {String} [format=dm]: Return Format 'd', 'dm', 'dms'
* @returns {String} Koordinaten String in dem festgelegten Format
* @throws {TypeError} wenn deg ein Object ist
*/
Geo.toDMS = function(deg, format) {
if (typeof deg == 'object') {
throw new TypeError('Geo.toDMS - deg is an object');
}
if (isNaN(deg)) {
return 'NaN';
} // give up here if we can't make a number from deg
// default value of format = dm
if (typeof format == 'undefined') {
format = 'dm';
}
deg = Math.abs(deg); // (unsigned result ready for appending NS|WE)
var dms,
d,
m,
s,
min,
sec,
tmpD,
tmpM;
switch (format) {
case 'd':
d = deg.toFixed(8); // round degrees
tmpD = d;
if (d < 100) {
tmpD = '0' + tmpD;
} // pad with leading zeros
if (d < 10) {
tmpD = '0' + tmpD;
}
dms = tmpD; // add ° symbol
break;
case 'dm':
min = (deg * 60).toFixed(8); // convert degrees to minutes & round
d = Math.floor(min / 60); // get component deg/min
m = (min % 60).toFixed(3); // pad with trailing zeros
tmpD = d;
tmpM = m;
if (d < 100) {
tmpD = '0' + tmpD;
} // pad with leading zeros
if (d < 10) {
tmpD = '0' + tmpD;
}
if (m < 10) {
tmpM = '0' + tmpM;
}
dms = tmpD + '\u00B0' + tmpM; // add ° symbol
break;
case 'dms':
sec = (deg * 3600).toFixed(0); // convert degrees to seconds & round
d = Math.floor(sec / 3600); // get component deg/min/sec
m = Math.floor(sec / 60) % 60;
s = (sec % 60).toFixed(0); // pad with trailing zeros
if (d < 100) {
d = '0' + d;
} // pad with leading zeros
if (d < 10) {
d = '0' + d;
}
if (m < 10) {
m = '0' + m;
}
if (s < 10) {
s = '0' + s;
}
dms = d + '\u00B0' + m + '\u2032' + s + '\u2033'; // add °, ', " symbols
break;
}
return dms;
};
/**
* Erzeugt einen Punkt mit den gegebenen Latitude und Longitude
* @constructor
* @param {Number} lat: latitude in numeric degrees
* @param {Number} lon: longitude in numeric degrees
*/
function LatLon(lat, lon) {
// only accept numbers or valid numeric strings
this._lat = typeof(lat) == 'number' ? lat : typeof(lat) == 'string' && lat.trim() != '' ? +lat : NaN;
this._lon = typeof(lon) == 'number' ? lon : typeof(lon) == 'string' && lon.trim() != '' ? +lon : NaN;
}
/**
* Gibt einen String mit "lat() lon()" von diesem Punkt zurück
*
* @param {String} [format]: Return value als 'd', 'dm', 'dms'
* @returns {String} Space-separated latitude/longitude
*
*/
LatLon.prototype.toString = function(format) {
if (typeof format == 'undefined') {
format = 'dm';
}
if (isNaN(this._lat) || isNaN(this._lon)) {
return '-,-';
}
return Geo.toLat(this._lat, format) + ' ' + Geo.toLon(this._lon, format);
};
/**
* Interpretiert eine Koordinaten Eingabe des Formats "N51° 12.123 E010° 23.123" oder "51.123 10.123" bzw. benutzt Geocoding API um die Koordinaten zu finden.
*
* @param {String} coord_string: Koordinaten in einem Format
* @param {Boolean} [force_Geocoding=false]: Wenn gesetzt sucht die Methode bei nicht numerischer Eingabe mittels Geocoding nach den Koordinaten
* @returns {LatLon} Koordinaten Object
*/
async function parseCoordinates(coord_string, force_Geocoding) {
// entferne alle "," in Koordinaten String
if (typeof coord_string == "string") {
coord_string = coord_string.replace(/,/g, ".");
}
var lat,
lon;
// regex for N51° 12.123 E12° 34.123
// ... or one or both coords are zero (N,S,E,W are missing): 00° 00.000 000° 00.000 | N 51° 12.123 000° 00.000 | 00° 00.000 E 012° 34.123
var regex_coord_ns = new RegExp(/(N|S)\s*(\d{1,2})\s*°\s*(\d{1,2}\.\d{3})|(0{2})\s*°\s*(0{2}\.0{3})/);
var regex_coord_ew = new RegExp(/(E|W)\s*(\d{1,3})\s*°\s*(\d{1,2}\.\d{3})|(0{3})\s*°\s*(0{2}\.0{3})/);
// regex for 51.123 12.123
var regex_coord_dec = new RegExp(/(-{0,1}\d{1,2}\.\d{3})\s*(-{0,1}\d{1,3}\.\d{3})/);
var result_coord_ns = regex_coord_ns.exec(coord_string);
var result_coord_ew = regex_coord_ew.exec(coord_string);
var result_coord_dec = regex_coord_dec.exec(coord_string);
// Koordinate ist keins der beiden numerischen Formate
if (!(result_coord_ns && result_coord_ew) && !result_coord_dec) {
// ... now only a geocoding service can help ...
if (force_Geocoding) {
// search in OSM data
var response = await promiseRequest('GET', "https://nominatim.openstreetmap.org/search?format=json&limit=1&q=" + coord_string);
var geocoding_obj = JSON.parse(response.responseText);
if (geocoding_obj.length === 0) { // no result found
return false;
}
lat = geocoding_obj[0].lat;
lon = geocoding_obj[0].lon;
return new LatLon(lat, lon);
} else {
return false;
}
} else if (result_coord_ns && result_coord_ew) {
// result_coord_ns[0] = "N51° 12.123"
// result_coord_ew[0] = "E010° 23.123"
lat = Geo.parseDMS(result_coord_ns[0]);
lon = Geo.parseDMS(result_coord_ew[0]);
return new LatLon(lat, lon);
} else {
// result enthält beide Teile der Koordinate
lat = Geo.parseDMS(result_coord_dec[1]);
lon = Geo.parseDMS(result_coord_dec[2]);
return new LatLon(lat, lon);
}
}
function distanceBetween(lat1, lon1, lat2, lon2) {
var R = 6371000; // meters (change this constant to get miles)
var dLat = (lat2 - lat1) * Math.PI / 180;
var dLon = (lon2 - lon1) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d;
}
/* helpers */
// for map page: return center and radius
var getMapCenterAndRadius = function() {
var newMap = false;
if (location.pathname === '/play/map') {
newMap = true;
}
var ret = {};
if (newMap) {
const searchParams = new URLSearchParams(window.location.search);
ret.center = {};
ret.center.lat = searchParams.has('mlat') ? searchParams.get('mlat') : searchParams.get('lat');
ret.center.lng = searchParams.has('mlng') ? searchParams.get('mlng') : searchParams.get('lng');
const zoom = searchParams.has('zoom') ? searchParams.get('zoom') : '14';
// https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to#answer-127949
var metersPerPx = 156543.03392*Math.cos(ret.center.lat/180*Math.PI)/Math.pow(2, zoom);
var $map = $('div.leaflet-container'); // leaflet
if ($map.length === 0) $map = $('div.gm-style'); // GM
var dim = Math.min($map.width(), $map.height());
ret.radius = (metersPerPx*(dim/2)/1000).toFixed(3); // km
} else { // old map
var map = unsafeWindow.MapSettings ? unsafeWindow.MapSettings.Map : undefined,
bounds,
ne = {},
sw = {};
if (typeof(map) !== "undefined") {
bounds = map.getBounds();
ret.center = {};
if (map.streetView) { // GM
ret.center.lat = map.getCenter().lat();
ret.center.lng = map.getCenter().lng();
ne.lat = bounds.getNorthEast().lat();
ne.lng = bounds.getNorthEast().lng();
sw.lng = bounds.getSouthWest().lng();
} else { // Leaflet
ret.center = map.getCenter();
ne.lat = bounds.getNorthEast().lat;
ne.lng = bounds.getNorthEast().lng;
sw.lng = bounds.getSouthWest().lng;
}
ret.radius = Math.floor( distanceBetween(ret.center.lat, ret.center.lng, ne.lat, ne.lng - (ne.lng - sw.lng)/2) )/1000;
}
}
return ret;
};
// is string json, isJSON(response.responseText)
// fn from js-Framework prototype v1.7
var isJSON = function(str) {
if (str.length === 0) {
return false;
}
str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
return (/^[\],:{}\s]*$/).test(str);
};
// is String a GCCode ?
// begin with 'GC' + 1 to 6 chars, current is 5 (01.2017)
// return Boolean
var isGCCode = function(gccode) {
return (/^\s*(GC[0-9A-Z]{1,6})\s*$/).test(gccode);
};
// find GCID (GCCode) in String
// first 'GC' + 1 to 6 chars in a string, current is 5 (01.2017)
// return String
// example: http://jsfiddle.net/NUFGq/15/
var findGCCodeFromString = function(str) {
if (!str || str.length === 0) {
return false;
}
var treffer = str.match(/\bGC([0-9A-Z]{1,6})\b/) || [];
return (treffer[0] || "");
};
// converts GS geocache code string of format GCxxxx to its cache ID
function gCCode2ID(wp) {
// in GS waypoints the letters I,L,O,S,U are missing;
// therefore, for the base-31 conversion, a shift of letters is required first
function shiftLettersInGCCode(wp) {
let char36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let char31 = "0123456789ABCDEFGHJKMNPQRTVWXYZ";
wp = wp.split(''); // string to char array
let i, c;
for (i=0; i<wp.length; i++) {
c = wp[i];
wp[i] = char36[char31.indexOf(c)];
}
return wp.join('');
}
wp = wp.substr(2); // remove leading 'GC' from waypoint
wp = shiftLettersInGCCode(wp); // shift letters of GC waypoint
let id = parseInt(wp,31); // base-31 to decimal conversion
// either use the base-31 or hex to decimal conversion
if (id > parseInt('FFFF',31)) // max. occurring hex value in GC waypoints
id = id - 411120; // [65536,...]
else
id = parseInt(wp,16); // [0,65535]
return id;
}
// retrieve stylesheet from document
function getStyleSheet(name) {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (sheet.href && sheet.href.includes(name)) {
return sheet;
}
}
}
// delete stylesheet rules by pattern
function deleteStyleSheetRules(sheet, pattern) {
var length = sheet.cssRules.length;
var deletedRules = [];
for (var i = 0; i < length; i++) {
var rule = sheet.cssRules[i];
if (pattern.test(rule.cssText)) {
deletedRules.push(rule.cssText);
sheet.deleteRule(i);
i -= 1;
length -= 1;
}
}
return deletedRules;
}
// get React fiber key from node
function getReactFiberKey(node) {
try { return Object.keys(node).find(o => o.includes('__reactFiber')); }
catch {
log('getReactFiberKey() failed for node\n' + node.outerHTML);
return undefined;
}
}
// get React page props from node
function getReactPageProps(node) {
try { return node[getReactFiberKey(node)].memoizedProps.children.props; }
catch {
log('getReactPageProps() failed for node\n' + node.outerHTML);
return undefined;
}
}
/* initialize */
// init core variables
function initCore() {
debug("initCore()");
// setting up the language
$.gctour.currentLang = GM_getValue('language', $.gctour.defaultLang);
// getting all tours
TOURS = loadValue('tours', []);
// go get the current tour from the tour list
var currentTourId = GM_getValue('currentTour', -1);
CURRENT_TOUR = getTourById(currentTourId);
// oh - there is no current tour!? create one!
if (!CURRENT_TOUR) {
CURRENT_TOUR = {};
CURRENT_TOUR.id = getNewTourId();
CURRENT_TOUR.name = "Tour " + CURRENT_TOUR.id;
CURRENT_TOUR.geocaches = [];
TOURS.push(CURRENT_TOUR);
log("found no currentTour! Creating new one: " + CURRENT_TOUR.id + " ; " + CURRENT_TOUR.name);
saveCurrentTour();
}
}
function init() {
debug("init()");
// set Styles (GM_addStyle)
initStyle();
// add global styles
var head = document.getElementsByTagName('head')[0],
style = document.createElement('style');
style.type = 'text/css';
head.appendChild(style);
// initialize tour list under main navigation
initComponents();
// handle window resize
$(window).on({
'resize': function() {
handleResize();
}
});
// update the complete gui if the browser tab gets active
$(document).on('visibilitychange', function() {
if (document.visibilityState === 'visible') {
updateTour();
}
});
// process "add to GCTour" link from GCTOUR_HOST
if (document.URL.search("webcode") >= 0) {
document.title = "GCTour";
document.getElementsByTagName('body')[0].innerHTML = "<div align='center'><a href='" + GS_HOST + "'><img src='" + $.gctour.img.gctourLogo + "'/></a></div>";
downloadTour(document.URL.split("webcode/")[1]);
return;
}
// process autoTour: loop through results on old search page and save autoTour
if (GM_getValue('tq_url')) {
processAutoTour();
}
// maps
// old (browse) map: /map/default.aspx
if (document.URL.search(GS_HOST+"map") >= 0) {
// add to tour in cache pop-up;
// setTimeout is necessary since GCLH2 changes this template, too
setTimeout(function() {
$('#cacheDetailsTemplate').text(
function(index, text) {
var tmpAddToTour = '{{#if $ctx.userIsLoggedIn() }}' +
'<a class="lnk" id="attKnopf" href="javascript:add2tour();">' +
'<img src="' + $.gctour.img.addToTour + '"><span>' + $.gctour.lang('cache.addToTour') + '</span>' +
'</a>';
return text.replace(/\{\{\#if \$ctx.userIsLoggedIn\(\) \}\}/g, tmpAddToTour);
}
);
}, 0);
var add2tour = function() {
setTimeout(function() {
var gccode = $('#gmCacheInfo div[class*="code"]:visible:first').text().trim();
var name = $("#gmCacheInfo a[href*='cache_details.aspx']:visible:first").text().trim();
var guid = getUrlVars($("#gmCacheInfo a[href*='/seek/log.aspx?guid']:visible:first").attr("href"))["guid"];
var imageUrl = $("#gmCacheInfo img[src*='images/mapicons/']:visible:first").attr("src") ||
$("#gmCacheInfo img[src*='" + GS_WPT_IMAGE_PATH + "']:visible:first").attr("src");
var cacheTypeImage = imageUrl.split('/').pop();
debug("map add2tour: gccode:'" + gccode + "' name:'" + name + "' image:'" + cacheTypeImage + "' guid:'" + guid + "'");
addGeocache(gccode, guid, name, cacheTypeImage)();
saveCurrentTourRefreshGUI();
}, 0);
};
// workaround for Violentmonkey in Firefox if "exportFunction" does not exist
// (see https://github.com/violentmonkey/violentmonkey/issues/168#issuecomment-321573665)
// nonetheless, this doesn't work for Chrome/Violentmonkey
if (typeof exportFunction !== 'function') {
exportFunction = function(func, scope, options) {
if (options && options.defineAs) {
scope[options.defineAs] = func;
}
return func;
};
}
exportFunction(add2tour, unsafeWindow, {
defineAs : "add2tour"
});
// make GS scrollbar in search section work
setTimeout(function() {
// show working scrollbar
$('div#scrollbar').css('overflow','');
// remove non-working scrollbar
$('div#scroller').next().remove();
},0);
}
// new (search) map: /play/map
if (document.URL.search("/play/map") >= 0) {
const searchButtonIsActive = () => $('button[data-testid="search-mode-toggle"]').attr('aria-selected') === 'true';
// add cache to tour from cache details in sidebar
const add2tour_cacheDetails = () => {
let gccode = $('span.cache-metadata-code:first').text().trim();
let name = $('div.header-top-left h1').text().trim();
let guid = undefined; // must be present, but not really needed
let type = $('p.status-and-type');
$('span.status', type).remove(); // remove status (if present): Premium, Disabled, ...
type = type.text().trim();
let ind = WPT_ARRAY.findIndex(obj => obj.gsDisplayName == type);
let cacheTypeImage = WPT_ARRAY[ind].wptTypeId + '.gif';
//debug("add2tour_cacheDetails: gccode:'" + gccode + "' name:'" + name + "' image:'" + cacheTypeImage + "' guid:'" + guid + "'");
addGeocache(gccode, guid, name, cacheTypeImage)();
saveCurrentTourRefreshGUI();
};
// add cache to tour from search list in sidebar
const add2tour_searchList = (cacheitem) => {
let gccode = cacheitem.code;
let name = cacheitem.name;
let guid = undefined;
let type = cacheitem.geocacheType;
let cacheTypeImage;
let ind = WPT_ARRAY.findIndex(obj => obj.wptTypeId == type);
cacheTypeImage = WPT_ARRAY[ind].wptTypeId + '.gif';
//debug("add2tour_searchList: gccode:'" + gccode + "' name:'" + name + "' image:'" + cacheTypeImage + "' guid:'" + guid + "'");
addGeocache(gccode, guid, name, cacheTypeImage)();
}
const addAllCachesFromSearchList = async (newTour) => {
if (newTour && !newTourFunction()()) {
return; // exit on cancel
}
try {
addProgressbar();
// get results
const node = $('div.page-container')[0];
const caches = getReactPageProps(node).searchResults.results;
for (let i = 0; i < caches.length; i++) {
add2tour_searchList(caches[i]);
}
// save and update tour, close overlay
saveCurrentTourRefreshGUI();
closeOverlay();
} catch(e) {
if (newTour) deleteCurrentTour(true);
addErrorDialog({
caption : "addAllCachesFromSearchList error",
_exception : e
});
}
}
const add2TourButton =
'<li>' +
'<button id="add2TourButton">' +
'<img class="action-icon" src="' + $.gctour.img.addToTour + '" style="width:20px;height:20px;">' +
'<span>' + $.gctour.lang('cache.addToTour') + '</span>' +
'</button>' +
'</li>';
// observer callback when sidebar switches between search list, bm lists and cache details view
const cb_sidebar = () => {
if ($('div[data-testid="gc-map-geocache-list-item"]')[0]) { // list view
// buttons to add all caches to current or new tour
// (only add when necessary and only if search button is active)
if ($('button#addAll2CurrentTourButton').length === 0 && searchButtonIsActive()) {
const addAll2CurrentTourButton =
'<button id="addAll2CurrentTourButton" class="border-1 border-solid border-grey-600/45 rounded hover:shadow-outline" style="cursor:pointer;margin-bottom:2px;padding:1px;background-color:white;" title="(' + $.gctour.lang('cache.all') + ') ' + $.gctour.lang('cache.addToCurrentTour').replace('<b>', '').replace('</b>', '') + '">' +
'<img src="' + $.gctour.img.addToTour + '" style="vertical-align: bottom;"/>' +
'</button>';
const addAll2NewTourButton =
'<button id="addAll2NewTourButton" class="border-1 border-solid border-grey-600/45 rounded hover:shadow-outline" style="cursor:pointer;margin-bottom:2px;padding:1px;background-color:white;" title="(' + $.gctour.lang('cache.all') + ') ' + $.gctour.lang('cache.addToNewTour').replace('<b>', '').replace('</b>', '') + '">' +
'<img src="' + $.gctour.img.newTour + '" style="vertical-align: bottom;"/>' +
'+' +
'<img src="' + $.gctour.img.addToTour + '" style="vertical-align: bottom;"/>' +
'</button>';
const divAddAll2TourButtons =
'<div style="display:grid;grid-template-columns:60px 30px;justify-items:end;justify-content:end;margin-bottom:-10px;">' +
addAll2NewTourButton +
addAll2CurrentTourButton +
'</div>';
// add buttons
$('button[data-testid="sort-option-menu-toggle"]').parent().parent().before(divAddAll2TourButtons);
// click events
$('button#addAll2CurrentTourButton').on('click', () => addAllCachesFromSearchList(false));
$('button#addAll2NewTourButton').on('click', () => addAllCachesFromSearchList(true));
// ensure that add2tour buttons are visible in search list
} else if (searchButtonIsActive()) {
$('button#addAll2NewTourButton').css('display','');
$('button#addAll2CurrentTourButton').css('display','');
}
} else if ($('div.cache-detail-preview').length > 0 ) { // cache details view
// hide add2tour buttons
$('button#addAll2NewTourButton').css('display','none');
$('button#addAll2CurrentTourButton').css('display','none');
if ($('button#add2TourButton').length === 0) { // only add once
// add "add2tour" button
$('div.cache-preview-action-menu ul').prepend(add2TourButton);
$('button#add2TourButton').on('click', () => add2tour_cacheDetails());
$('button#add2TourButton').parent().parent().css('grid-template-columns', 'repeat(4, 1fr)');
}
} else if (!searchButtonIsActive()) { // bm list view
// hide add2tour buttons
$('button#addAll2NewTourButton').css('display','none');
$('button#addAll2CurrentTourButton').css('display','none');
}
}
// start observing sidebar for switches between search list, bm lists and cache details view
waitForDOMelementThenRun('div#sidebar', () => {
const observer_sidebar = new MutationObserver(cb_sidebar);
const target_sidebar = $('div#sidebar')[0];
const config_sidebar = {
childList: true,
subtree: true
};
observer_sidebar.observe(target_sidebar, config_sidebar);
});
}
// old (browse) and new (search) map: add autoTour button
if (document.URL.search(GS_HOST+"map") >= 0 || document.URL.search("/play/map") >= 0) {
const addAutoTourButton = () => {
let $li = $("<li>", {
"css": {
'height': "26px",
'overflow': "hidden",
'border-radius': "5px",
'background-color': "#FFF",
'border': "2px solid #999",
'cursor': 'pointer'
},
"html": $("<img>", {
"id": "autoTourButton",
"title": "Map to autoTour",
"src": $.gctour.img.mapToAutoTour,
"width": "70px",
"vertical-align": "top"
})
})
.on("click", function() {
const map = getMapCenterAndRadius();
showAutoTourDialog(map.center, map.radius);
})
.on("mouseenter", function() {
$(this).css({ 'backgroundColor': 'orange' });
})
.on("mouseleave", function() {
$(this).css({ 'backgroundColor': '#FFF' });
});
let $autoTourAnchor = $("li a.message-center").parent().parent();
// GCLH2 replaces site header, therefore another anchor is necessary
// (we need to wait until GCLH2 header is ready)
setTimeout(() => {
// add button only once (due to timeout, check must be here)
if ($('#autoTourButton')[0]) return;
if ($('gclh_nav').length>0) {
if ($("li.messagecenterheaderwidget").length>0) { // message center
$autoTourAnchor = $("li.messagecenterheaderwidget");
$li.insertAfter($autoTourAnchor);
}
// message center removed by GCLH
else if ($("li.li-user").length>0) { // user section
$autoTourAnchor = $("li.li-user");
$autoTourAnchor.css("margin-left", "0px");
$li.insertBefore($autoTourAnchor);
}
// necessary style adaptations for GCLH2 header
//$('img',$li).css("margin-bottom", "6px"); // autoTour image
$autoTourAnchor.next().css({"margin-left": "5px", "margin-right": "-35px"});
} else {
$li.insertAfter($autoTourAnchor);
}
},1000);
}
waitForDOMelementThenRun("li a.message-center", () => addAutoTourButton());
}
// helper function to add buttons to bookmark and old search list
const addButtonsToAddAllOrSelectedCaches = function(addEntriesFromList) {
let selectMenu =
'<select name="add2tour" id="add2tour">' +
' <option value="all">' + $.gctour.lang("cache.shown") + '</option>' +
' <option value="selected">' + $.gctour.lang("cache.marked") + '</option>' +
'</select>';
// buttons
let $buttons = $("<div>", {
"css" : {
"margin" : "10px 0 10px 0",
"float": "left",
"width": "100%"
},
html : selectMenu
})
.append(
// button to add caches to current tour
$("<button>", {
"css" : {
"margin-left" : 10,
"cursor" : "pointer",
"background-color" : "#EEE"
},
"html" : "<img src='" + $.gctour.img.addToTour + "'/>&nbsp;" + $.gctour.lang('cache.addToCurrentTour')
})
.on('click', {
bLs: ($bookmarkLines) ? $bookmarkLines : '',
newTour: false
}, function(e) {
e.data["checkedOnly"] = ($("select#add2tour option:selected").text() === $.gctour.lang('cache.marked'));
e.preventDefault();
addEntriesFromList(e);
}))
.append(
// button to add caches to a new tour
$("<button>", {
"css" : {
"margin-left" : 10,
"cursor" : "pointer",
"background-color" : "#EEE"
},
"html" : "<img src='" + $.gctour.img.newTour + "'/>&nbsp;+&nbsp;<img src='" + $.gctour.img.addToTour + "'/>&nbsp;" + $.gctour.lang('cache.addToNewTour')
})
.on('click', {
bLs: ($bookmarkLines) ? $bookmarkLines : '',
newTour: true
}, function(e) {
e.data["checkedOnly"] = ($("select#add2tour option:selected").text() === $.gctour.lang('cache.marked'));
e.preventDefault();
addEntriesFromList(e);
})
);
return $buttons;
}
// add buttons to bookmark list
if (document.URL.search("/plan/lists") >= 0) {
// wait until bml is loaded
const anchor = '.list-geocache-row'; // row identifier
const timeout = 300000; // 5min
waitForDOMelementThenRun(anchor, addButtons, timeout);
var $bookmarkLines; // must be global
function addButtons() {
// all bml caches from current active page
$bookmarkLines = $(anchor);
// if buttons were already added, nothing to be done
if ($('img', $('[data-testid="gc-checkbox"]').parent()).length === $bookmarkLines.length) {
// continue observing
startObserver();
return;
}
// buttons to add single caches to tour
let checkbox;
for (let i = 0; i < $bookmarkLines.length; i++) {
// if button was already added, nothing to be done
if ($('#gct_add2Tour_' + (i+1), $bookmarkLines)[0]) continue;
checkbox = $('[data-testid="gc-checkbox"]', $bookmarkLines[i])[0];
// add button
$("<img>", {
"id": 'gct_add2Tour_' + (i + 1),
"alt": $.gctour.lang('cache.addToTour'),
"title": $.gctour.lang('cache.addToTour'),
"src": $.gctour.img.addToTour,
"css": {
"cursor": "pointer",
"float": "left",
"margin-top": "5px",
"width": "20px",
"height": "20px"
}
}).on('click', { entry: $bookmarkLines[i] }, function(e) {
let props = getCacheProps(e.data.entry);
addGeocache(props.id, props.guid, props.name, props.image)();
saveCurrentTourRefreshGUI();
}).insertAfter(checkbox); // add button below checkbox
}
// buttons to add marked or all caches in list to current or new tour
if ($('select#add2tour').length == 0) {
let $buttons = addButtonsToAddAllOrSelectedCaches(addEntryFromBookmark);
$buttons.insertBefore($('.gc-page-controls').parent());
$('select#add2tour').css({ "padding-top": "4px", "padding-bottom": "4px" });
}
// for pagination and sorting the buttons need to be updated
startObserver();
}
// helper functions
function startObserver() {
const cb = (_mutationList, observer) => {
observer.disconnect();
waitForDOMelementThenRun(anchor, addButtons, timeout);
};
const observer = new MutationObserver(cb);
// observe page for changes of child nodes
const target = $('div.page-container')[0];
const config = {
childList: true,
subtree: true
};
if (target) observer.observe(target, config);
}
function addEntryFromBookmark(e) {
const ck = e.data.checkedOnly || false;
const nt = e.data.newTour || false;
let listName;
try {
listName = nt ? $('span.listhub-back-link').next()[0].innerText.split('\n')[0].trim() : "";
} catch {
listName = nt ? "Tour" : "";
}
if (!nt || (nt && newTourFunction(listName)())) {
$('.list-geocache-row').each((_i, row) => {
const entry = getCacheProps(row);
if (entry && (!ck || (ck && entry.checked))) {
addGeocache(entry.id, entry.guid, entry.name, entry.image)();
}
});
saveCurrentTourRefreshGUI();
}
}
function getCacheProps(row) {
let props = {};
const $a = $('a', row);
props.id = $a.attr('href').split('/').at(-1);
props.guid = '';
props.name = $a.html();
const type = $('svg.geocache-type-icon use', row).attr("href").replace("#", "").replace("_disabled", "");
const ind = WPT_ARRAY.findIndex(obj => obj.shortname == type);
props.image = (ind !== -1) ? WPT_ARRAY[ind].wptTypeId + '.gif' : '2.gif'; // tradi icon as fallback
props.checked = $('div[data-testid="gc-checkbox"] input', row).is(':checked');
return props;
}
}
// add buttons to old search
if (document.URL.search("/seek/nearest.aspx") >= 0) {
// wait until all wpt type images are loaded
waitForDOMelementThenRun('.SearchResultsWptType', () => {
var entry_i,
entry,
entries = getEntriesFromOldSearchpage();
for (entry_i = 0; entry_i < entries.length; entry_i++) {
entry = entries[entry_i];
$("<img>", {
"alt" : $.gctour.lang('cache.addToTour'),
"title" : $.gctour.lang('cache.addToTour'),
"src" : $.gctour.img.addToTour,
"css" : {
"cursor" : "pointer",
"float": "left",
"margin-left": "3px"
}
})
.on('click', {
entry : entry
}, function(e) {
addGeocache(e.data.entry.id, e.data.entry.guid, e.data.entry.name, e.data.entry.image)();
saveCurrentTourRefreshGUI();
})
.appendTo(entry.addBtnPosition);
// keep button in same line
entry.addBtnPosition.style.whiteSpace = 'nowrap';
}
// helper function
var addEntryFromSearchpage = function(e) {
var i, entry;
var ck = e.data.checkedOnly || false;
var nt = e.data.newTour || false;
var entries = getEntriesFromOldSearchpage();
if (!nt || (nt && newTourFunction()())) {
for (i = 0; i < entries.length; i++) {
entry = entries[i];
if ((entry) && (!ck || (ck && entry.checked))) {
addGeocache(entry.id, entry.guid, entry.name, entry.image)();
}
}
saveCurrentTourRefreshGUI();
}
};
// buttons to add marked or all caches in list to current or new tour
let $buttons = addButtonsToAddAllOrSelectedCaches(addEntryFromSearchpage);
$buttons.prependTo("div#ctl00_ContentBody_ResultsPanel");
});
}
// add buttons to new search page
if (document.URL.search("/play/results") >= 0) {
// wait until search results are loaded
// (for empty searches a longer timeout is necessary)
const timeout = 300000; // 5min
const anchor = 'li[data-testid="gc-search-results-list-item"]'; // row identifier
const addButtons = () => {
// all search results from current active page
let $rows = $(anchor);
// buttons to add single caches to tour
let column;
for (let i = 0; i < $rows.length; i++) {
// if add to tour button is already present, do nothing
if ($('#gct_add2Tour', $rows[i])[0]) continue;
// cache icon column
column = $('.geocache-icon-cell', $rows[i])[0];
// add button
$("<img>", {
"id": 'gct_add2Tour',
"alt": $.gctour.lang('cache.addToTour'),
"title": $.gctour.lang('cache.addToTour'),
"src": $.gctour.img.addToTour,
"css": {
"cursor": "pointer",
"margin": "0"
}
}).on('click', {
entry: $rows[i]
}, function(e) {
let props = getCacheProps(e.data.entry);
addGeocache(props.id, props.guid, props.name, props.image)();
saveCurrentTourRefreshGUI();
}).appendTo(column);
}
// buttons to add all caches to tour
$('div#gct_addAll2TourButtons').remove();
let addAll2CurrentTourButton =
'<button id="gct_addAll2CurrentTourButton" class="gc-button" style="cursor: pointer" title="(' + $rows.length + ' caches) ' + $.gctour.lang('cache.addToCurrentTour').replace('<b>', '').replace('</b>', '') + '">' +
'<img src="' + $.gctour.img.addToTour + '" style="vertical-align: bottom;"/>' +
'</button>';
let addAll2NewTourButton =
'<button id="gct_addAll2NewTourButton" class="gc-button" style="cursor: pointer" title="(' + $rows.length + ' caches) ' + $.gctour.lang('cache.addToNewTour').replace('<b>', '').replace('</b>', '') + '">' +
'<img src="' + $.gctour.img.newTour + '" style="vertical-align: bottom;"/>' +
'+' +
'<img src="' + $.gctour.img.addToTour + '" style="vertical-align: bottom;"/>' +
'</button>';
let divAddAll2TourButtons =
'<div id="gct_addAll2TourButtons">' +
addAll2NewTourButton +
addAll2CurrentTourButton +
'</div>';
// add buttons in 1st column of pagination row (Note: GS buttons may change in mobile layout, this position is safe)
$('div.gc-pagination').parent().siblings().first().append($(divAddAll2TourButtons));
// click events
$('#gct_addAll2CurrentTourButton').on('click', () => addAllCachesToTour($rows, false));
$('#gct_addAll2NewTourButton').on('click', () => addAllCachesToTour($rows, true));
// observe search results
startObserver();
}
const startObserver = () => {
const cb = (_mutationList, observer) => {
observer.disconnect();
waitForDOMelementThenRun(anchor, addButtons, timeout);
}
// observe search results for changes of child nodes
const target = $('div[data-testid="gc-search-results-grid"]')[0],
config = {
childList: true,
subtree: true
},
observer = new MutationObserver(cb);
if (target) observer.observe(target, config);
}
waitForDOMelementThenRun(anchor, addButtons, timeout);
// helper functions
const getCacheProps = (row) => {
let props = {};
props.id = $('.code-display', row).text();
props.guid = '';
props.name = $('.name-display', row).text();
let type = $('use[data-testid="gc-geocache-icon-svg"]', row).attr("href").replace("#", "").replace("_disabled", "");
let ind = WPT_ARRAY.findIndex(obj => obj.shortname == type);
props.image = (ind !== -1) ? WPT_ARRAY[ind].wptTypeId + '.gif' : '2.gif'; // tradi icon as fallback
return props;
}
const addAllCachesToTour = (rows, newTour) => {
if (newTour && !newTourFunction()()) {
return; // exit on cancel
}
const str = "(" + $(anchor).length + " caches) " + $.gctour.lang('cache.addToCurrentTour').replace('<b>', '').replace('</b>', '') + "?";
if (!newTour && !confirm(str)) {
return; // exit on cancel
}
let props;
for (let i = 0; i < rows.length; i++) {
props = getCacheProps(rows[i]);
addGeocache(props.id, props.guid, props.name, props.image)();
}
saveCurrentTourRefreshGUI();
}
}
// add buttons to cache details page
if (document.URL.search(GS_HOST + "geocache/GC") >= 0 || document.URL.search("cache_details.aspx") >= 0) {
initButton();
}
}
/* init gui */
function initButton() {
// if we are on a cache page the buttonGroup != null - so add the 'to tour'-button
var cacheControl = $("div.CacheInformationTable:first");
if (cacheControl.length > 0) {
var div_element = createElement('div', {
style : "border-top: 1px solid rgb(192, 206, 227);"
});
cacheControl.append(div_element);
var gcTourFieldset = createElement('fieldset', {
style : "background-color: #EFF4F9;border-color: #C0CEE3 !important;margin-top:0;padding: 0.5em;"
});
append(gcTourFieldset, div_element);
gcTourFieldset.setAttribute('class', 'dialogFooter');
gcTourFieldset.innerHTML = "<legend class='note' style='background:url(\"" + $.gctour.img.gctourLogoSmall + "\") no-repeat scroll 0 0 transparent;padding-left:20px;'>GCTour</legend>";
// 1) add to tour button
var newButton = createElement('input', {
type : "button",
value : " " + $.gctour.lang('cache.addToTour'),
style : "float:left;background-image:url(" + $.gctour.img.addToTour + ")"
});
append(newButton, gcTourFieldset);
newButton.setAttribute('onclick', 'return false;');
// locate the values and save it
var minimal_geocache = getMinimalGeocacheDetails(document.getElementsByTagName('html')[0]);
var cacheId = minimal_geocache.gccode;
var guidId = minimal_geocache.guid;
var cacheName = minimal_geocache.name;
var cacheTypeImage = minimal_geocache.type;
// on click add an element
newButton.addEventListener('click', function() {
addGeocache(cacheId, guidId, cacheName, cacheTypeImage)();
saveCurrentTourRefreshGUI();
}, false);
// 2) direct print button
newButton = createElement('input', {
type : "button",
value : " " + $.gctour.lang('cache.directPrint'),
style : "float:left;background-image:url(" + $.gctour.img.printer + ")"
});
append(newButton, gcTourFieldset);
newButton.setAttribute('onclick', 'return false;');
// on click add an element
newButton.addEventListener('click', function() {
var entry = {};
entry.id = cacheId;
entry.name = escapeHTML(cacheName);
entry.guid = guidId;
entry.image = GS_HOST + GS_WPT_IMAGE_PATH + cacheTypeImage;
var temp_tour = {};
temp_tour.name = entry.name;
temp_tour.geocaches = [entry];
printPageFunction(temp_tour);
}, false);
append(newButton, gcTourFieldset);
// 3) change coordinates button
newButton = createElement('input', {
type : "button",
value : " " + $.gctour.lang('cache.moveCoords'),
style : "float:left;background-image:url(" + $.gctour.img.coord_update + ")"
});
append(newButton, gcTourFieldset);
newButton.setAttribute('onclick', 'return false;');
newButton.addEventListener('click', openChangeCoordinates, false);
append(newButton, gcTourFieldset);
// display changed coordinates (if they have been changed by GCTour)
if (GM_getValue('coords_' + cacheId, "null") != "null") {
var coords_cacheId = GM_getValue('coords_' + cacheId);
displayChangedCoordinates(new LatLon(coords_cacheId.split('#')[0], coords_cacheId.split('#')[1]).toString());
}
}
}
// the tour list under main navigation
function initComponents() {
// gcTour Button
$("<div>", {
id : "gctourButtonWrapper",
"class" : "header gctour-grand-default",
"html" :
$("<img>", {
"src" : $.gctour.img.gctourLogoSmall
})
})
.on("mouseenter", function () {
$(this).addClass('gctour-grand-hover');
$("#gctourContainer").animate({
left : 0
}, 500);
})
.on("mouseleave", function () {
$(this).removeClass('gctour-grand-hover');
})
.appendTo("body");
// gcTour Container
$("<div>", {
id : "gctourContainer",
"css" : {
left : (STICKY) ? 0 : -210
}
})
.on("mouseenter", function () {
clearTimeout(TIMEOUT);
})
.on("mouseleave", function () {
if (!STICKY) {
TIMEOUT = setTimeout(function () {
$("#gctourContainer").animate({
left: -210
}, 500);
}, 1000);
}
})
.appendTo("body");
var $geocacheList = $('<div>', {
id : "gctour_geocacheList",
"css" : {
overflow : 'auto',
height : '80%',
width : '100%'
},
"html" :
$('<ul>', {
id : "cacheList",
'class' : 'cachelist'
})
.sortable({
axis : 'y',
placeholder : 'ui-sortable-placeholder',
opacity : 0.8,
revert : true,
start : function(e, ui) {
// save old position
$(this).data('old-pos', ui.item.index());
},
stop : function(e, ui) {
// init
var newPos = ui.item.index();
var oldPos = $(this).data('old-pos');
$(this).removeData('old-pos');
debug("Drag n Drop in progress:\n" +
"\tMove " + CURRENT_TOUR.geocaches[oldPos].name + " (" + ui.item.attr('id') + ") from '" + oldPos + "' to '" + newPos + "'");
// ignore the same position
if (oldPos === newPos) {
return;
}
// determine positions
var insertPos = (oldPos > newPos) ? newPos : newPos + 1;
var removePos = (oldPos < newPos) ? oldPos : oldPos + 1;
// changing the position
CURRENT_TOUR.geocaches.splice(insertPos, 0, CURRENT_TOUR.geocaches[oldPos]);
CURRENT_TOUR.geocaches.splice(removePos, 1);
// ... and save the new tour object
setTimeout(function() { // hack to prevent "access violation" from Greasemonkey
saveCurrentTour();
}, 0);
move(ui.item.attr('id'), 0); // force numbering update
}
})
.disableSelection()
});
var webcodelink = GCTOUR_HOST + 'tour/' + (CURRENT_TOUR.webcode ? CURRENT_TOUR.webcode.trim() : '');
var $tourHeader = $("<div>", {
id : "gctour_tourHeader",
"css" : {},
"html" : '<u id="tourName">' + CURRENT_TOUR.name + '</u>&nbsp;<span style="font-size:66%" id="cachecount">(' + CURRENT_TOUR.geocaches.length + ')</span>' +
'<span id="webcode" style="display:' + ((!CURRENT_TOUR.webcode) ? "none" : "inline") + ';"><br/>' +
'Webcode:&nbsp;&nbsp;<b><a href="' + webcodelink + '" title="' + $.gctour.lang('container.tourHeader.makeMap.caption') + '" target="_blank">' + CURRENT_TOUR.webcode + '</a></b>&nbsp;</span><br/>'
});
// copy webcode
let copyImage = document.createElement('img');
copyImage.alt = $.gctour.lang('general.copy');
copyImage.title = $.gctour.lang('general.copy');
copyImage.src = $.gctour.img.copy;
copyImage.style.cursor = 'pointer';
copyImage.style.width = '11px';
copyImage.style.marginLeft = '2px';
copyImage.addEventListener('click', function() {
copyToClipboard($('span#webcode a').prop('href').split('/').pop(-1));
}, true);
addOpacityEffects(copyImage);
$('span#webcode',$tourHeader).append(copyImage);
$tourHeader.append(
// rename
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.edit,
title : $.gctour.lang('general.rename'),
alt : $.gctour.lang('general.rename')
}).on('click', function () {
var newTourName = prompt($.gctour.lang('tour.newDialog'), CURRENT_TOUR.name);
if (!newTourName) {
return;
}
CURRENT_TOUR.name = newTourName;
saveCurrentTour();
updateTour();
}),
// print
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.printer,
title : $.gctour.lang('settings.printview.header'),
alt : $.gctour.lang('settings.printview.header')
}).on('click', function () {
printPageFunction(CURRENT_TOUR);
}),
// sendToGPS
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.sendGPS,
title : $.gctour.lang('container.tourHeader.sendToGps'),
alt : $.gctour.lang('container.tourHeader.sendToGps')
}).on('click', function () {
send2Garmin();
}),
// downloadGPX
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.downloadGPX,
title : $.gctour.lang('container.tourHeader.downloadGpx'),
alt: $.gctour.lang('container.tourHeader.downloadGpx')
}).on('click', function () {
downloadGPXFunction();
}),
// send2cgeo
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.send2cgeo,
title : $.gctour.lang('send2cgeo.title'),
alt : $.gctour.lang('send2cgeo.title'),
'style': (SEND2CGEO) ? '' : 'display:none;'
}).on('click', function () {
openGcTour2cgeoDialog();
}),
// saveAsBookmarklist (PMO)
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.saveAsBookmarklist,
title : $.gctour.lang('container.tourHeader.saveAsBookmarklist.title'),
alt : $.gctour.lang('container.tourHeader.saveAsBookmarklist.title'),
'style': (isPremiumUser()) ? '' : 'display:none;'
}).on('click', function () {
saveAsBookmarklist();
}),
// makeMap
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.map,
title : $.gctour.lang('container.tourHeader.makeMap.caption'),
alt: $.gctour.lang('container.tourHeader.makeMap.caption')
}).on('click', function () {
makeMapFunction();
}),
// uploadTour
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.upload,
title : $.gctour.lang('container.tourHeader.upload.caption'),
alt: $.gctour.lang('container.tourHeader.upload.caption')
}).on('click', function () {
uploadTourFunction(CURRENT_TOUR.id);
}),
// addWaypoint
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.plus,
title : $.gctour.lang('container.tourHeader.addOwnWaypoint'),
alt: $.gctour.lang('container.tourHeader.addOwnWaypoint')
}).on('click', function () {
showNewMarkerDialog();
}),
// sort tour alphabetically
$('<img>', {
'class': 'tourImage',
src: $.gctour.img.sort,
title: $.gctour.lang('container.tourHeader.sortAlphabetically'),
alt: $.gctour.lang('container.tourHeader.sortAlphabetically'),
'style': 'margin-left:0px;'
}).on('click', function () {
sortCurrentTourAlphabetically();
}),
// deleteTour
$('<img>', {
id : 'gctourDeleteButton',
'class' : 'tourImage',
src : $.gctour.img.del,
title : $.gctour.lang('tour.delete'),
alt : $.gctour.lang('tour.delete'),
css : {
'display': (TOURS.length <= 1) ? 'none' : 'inline',
'margin-left': '0px',
'margin-right': '0px'
}
}).on('click', function () {
deleteCurrentTour();
})
).find("img.tourImage").addShadowEffect().addOpacityEffect();
var $toolbar = $("<div>", {
id : "gctour_toolbar",
"css" : {
height : 20,
'-moz-user-select' : "none"
}
});
$toolbar.append(
// newTourButton
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.newTour,
title : $.gctour.lang('container.toolbar.newList'),
alt : $.gctour.lang('container.toolbar.newList')
}).on('click', function () {
newTourFunction()();
}),
// toggleTourListButton
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.openTour,
title : $.gctour.lang('container.toolbar.openTour'),
alt : $.gctour.lang('container.toolbar.openTour')
}).on('click', function () {
openTourDialog();
}),
// downloadButton
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.download,
title : $.gctour.lang('container.toolbar.downloadTour.caption'),
alt: $.gctour.lang('container.toolbar.downloadTour.caption')
}).on('click', function () {
downloadTourDialog();
}),
// autoTourButton
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.autoTour,
title : $.gctour.lang('autoTour.title'),
alt: $.gctour.lang('autoTour.title')
}).on('click', function () {
var gooMap = getMapCenterAndRadius();
showAutoTourDialog(gooMap.center, gooMap.radius);
}),
// toggleSettingsButton
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.settings,
title : $.gctour.lang('container.toolbar.showSettings'),
alt: $.gctour.lang('container.toolbar.showSettings')
}).on('click', function () {
openSettingsDialog();
}),
// save settings info
$('<img>', {
'class': 'tourImage',
src: $.gctour.img.save,
title: $.gctour.lang('container.toolbar.saveSettings'),
alt: $.gctour.lang('container.toolbar.saveSettings')
}).on('click', function () {
openSettingsDialog();
$("#tabs").tabs({ active: 5 });
}),
// GCTour Home
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.globe,
title : $.gctour.lang('container.toolbar.homepage'),
alt : $.gctour.lang('container.toolbar.homepage'),
style: "float: right; margin-right: 11px;"
}).on('click', function () {
GM_openInTab(GCTOUR_HOST, false);
}),
// changelog
$('<img>', {
'class': 'tourImage',
src: $.gctour.img.info,
title: 'Changelog',
alt: 'Changelog',
style: "float: right;"
}).on('click', function () {
GM_openInTab("https://gist.github.com/DieBatzen/5814dc7368c1034470c8/raw/gctour.zChangelog.txt", false);
})
);
// Greasemonkey info
if (IS_GREASEMONKEY) {
$toolbar.append(
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.danger,
title : 'Greasemonkey Info',
alt : 'Greasemonkey Info',
style : "float: right;",
click : function() {
gmNotice();
}
})
);
}
var $header = $("<div>", {
id : "gctour_header",
"class" : "header gctour-grand-default" + ((STICKY) ? " gctour-grand-hover" : ""),
"css" : {
height : 40,
'cursor' : "pointer",
'-moz-user-select' : "none"
},
"html": "<img src='" + $.gctour.img.gctourLogo + "' style='margin: 6px 0px 0px;'/><small style='font-size: 65%;color: gray;vertical-align: bottom;'>v " + VERSION + "</small>" +
"<img id='gcTourPin' style='float:right;margin: 6px 2px 0 0;' src='" + ((STICKY) ? $.gctour.img.pinned : $.gctour.img.pin) + "'>"
})
.on("click", function () {
STICKY = !STICKY;
GM_setValue('sticky', STICKY);
$("img#gcTourPin").attr("src", ((STICKY) ? $.gctour.img.pinned : $.gctour.img.pin));
})
.on("mouseenter", function () {
$(this).addClass('gctour-grand-hover');
})
.on("mouseleave", function () {
if (!STICKY) {
$(this).removeClass('gctour-grand-hover');
}
});
$("#gctourContainer").append(
$header,
$toolbar,
$tourHeader,
$geocacheList
);
// if available, add link to bookmark list
if (CURRENT_TOUR.bml) {
let bml = new Bookmarklist(CURRENT_TOUR.bml);
bml.addLink();
}
// populate the current list on load
for (var i = 0; i < CURRENT_TOUR.geocaches.length; i++) {
addToCacheList(CURRENT_TOUR.geocaches[i], false);
}
if (CURRENT_TOUR.geocaches.length <= 0) {
var table = document.getElementById('cacheList');
table.innerHTML = $.gctour.lang('tour.empty');
}
// finally: set new heights and layout!
setTimeout(function() { // ensure that everything is loaded before resizing
handleResize();
}, 500);
}
/* init gui utilities*/
// get entries from old search page: https://www.geocaching.com/seek/nearest.aspx
function getEntriesFromOldSearchpage(page=document) {
// Data Rows without header
// <tr class="SolidRow Data BorderTop"> and
// <tr class="AlternatingRow Data BorderTop">
var q = $("table.SearchResultsTable tbody tr.Data",page);
var entries = q.map(function() {
var entryTds = $(this).find('td');
var entry = {};
// use waypoint type column as reference for other columns (GCLH2 may add an additional column for fav score, send2cgeo for adding caches)
var td = entryTds.find(".SearchResultsWptType").closest("td")[0];
var tdWpttype = entryTds.index(td);
// checkbox
entry.checked = entryTds.eq(0).find("input:checkbox:first").is(':checked');
// add2tour button position
entry.addBtnPosition = entryTds[0];
// favorites (do not change since other scripts may add columns)
entry.favorites = entryTds.find("span[id$='FavoritesValue']").text();
// cache type image
entry.image = entryTds.eq(tdWpttype).find("img:first").attr("src").toLowerCase().split('/').pop();
// cache type
entry.type = entry.image.split('.')[0];
entry.type = (entry.type == "earthcache") ? 137 : entry.type;
// GC code
entryTds.eq(tdWpttype + 1).find("span").eq(1).text().search(/\|\s*GC(\S{2,9})\s*\|/);
entry.id = "GC" + RegExp.$1;
// cache name
var lnk = entryTds.eq(tdWpttype + 1).find("a.lnk:first");
entry.name = lnk.text().trim();
// cache is available
entry.available = (lnk.css('text-decoration') !== "line-through");
// pmo
entry.pm_only = (entryTds.eq(tdWpttype + 2).find("img[src$='premium_only.png']").length > 0);
// D/T
var dt = entryTds.eq(tdWpttype + 3).find('span.small').text().trim();
entry.difficulty = dt.split("/")[0];
entry.terrain = dt.split("/")[1];
// cache size (do not change since GCLH2 may move the size image to a new column)
entry.size = entryTds.find('img[src*="/images/icons/container/"]:first').attr("src").split("/")[4].split(".")[0].trim();
/*
debug(
"getEntriesFromOldSearchpage cache row: " + "\n" +
"\tid:\t\t" + entry.id + "\n" +
"\tname:\t\t" + entry.name + "\n" +
"\tavailable:\t" + entry.available + "\n" +
"\timage:\t\t" + entry.image + "\n" +
"\tsize:\t\t" + entry.size + "\n" +
"\ttype:\t\t" + entry.type + "\n" +
"\tdifficulty:\t" + entry.difficulty + "\n" +
"\tterrain:\t" + entry.terrain + "\n" +
"\tfavorites:\t" + entry.favorites + "\n" +
"\tpm_only:\t" + entry.pm_only + "\n" +
"\tchecked:\t" + entry.checked + "\n");
*/
return entry;
}).get();
return entries;
}
/* gui functions */
function isLoggedIn() {
if (GS_USERINFO.isLoggedIn && window.location.href.includes("geocaching.com")) {
return true;
} else {
let str = '<div>' + $.gctour.lang('general.notLoggedIn') + '</div>';
showNotification({ text: str, style: "red" });
return false;
}
}
function isEmptyTour(tour) {
if (tour.geocaches.length > 0) {
return false;
} else {
let str = '<div>' + $.gctour.lang('tour.empty') + '</div>';
showNotification({ text: str, style: "yellow" });
return true;
}
}
function showGeocacheNotification(geocache, event) {
geocache.name = escapeHTML(geocache.name);
if (event.type == "success") {
$.gctour.notification.add({
title : $.gctour.lang('notifications.addgeocache.success.caption').format(geocache.id),
text : $.gctour.lang('notifications.addgeocache.success.content').format(geocache.tourname, geocache.name),
icon : geocache.image,
style : "green"
});
} else if (event.type == "contains") {
$.gctour.notification.add({
title : $.gctour.lang('notifications.addgeocache.contains.caption').format(geocache.id),
text : $.gctour.lang('notifications.addgeocache.contains.content').format(geocache.tourname, geocache.name),
icon : geocache.image,
style : "yellow"
});
} else {
$.gctour.notification.add({
title : "ERROR",
text : "Event '" + event + "' is not supported!",
style : "red"
});
}
}
function showNotification(options) {
options.title = "GCTour Info";
$.gctour.notification.add(options);
// adapt width slightly
$('#gctour-notification-box, #gctour-notification-box li',).css('width', '275px');
}
function handleResize() {
// Change the height of the container and Cache List
var container = $(window).height() - 30,
header = $("#gctourContainer #gctour_header").height(), // 40
toolbar = $("#gctourContainer #gctour_toolbar").height(), // 20;
tourheader = $("#gctourContainer #gctour_tourHeader").height(),
cachelist = container - (header + toolbar + tourheader /*+ footer*/);
// set the container height
$('#gctourContainer').css("height", container);
// set the cachelist height
$('#cacheList').parent().css("height", cachelist);
/*log(
"handleResize change height:\n" +
"\tcontainer: " + container + "\n" +
"\theader: " + header + "\n" +
"\ttoolbar: " + toolbar + "\n" +
"\ttourheader: " + tourheader + "\n" +
//"\tfooter: " + footer + "\n" +
"\t => cachelist: " + cachelist);*/
}
function updateGUI() {
var cacheList, i;
// update the cache count
updateCacheCount(CURRENT_TOUR.geocaches.length);
// update tourName
$("#tourName").html(CURRENT_TOUR.name);
// update webcode
var $webcode = $("#webcode");
if (CURRENT_TOUR.webcode) {
$webcode
.find("a:first")
.attr('href', GCTOUR_HOST + 'tour/' + CURRENT_TOUR.webcode.trim())
.text(CURRENT_TOUR.webcode)
.end()
.show();
} else {
$webcode.hide();
}
// update bookmark list
$('span#gct_bml').remove();
if (CURRENT_TOUR.bml) {
let bml = new Bookmarklist(CURRENT_TOUR.bml);
bml.addLink();
}
cacheList = $('#cacheList');
cacheList.html("");
// populate the current list on load
for (i = 0; i < CURRENT_TOUR.geocaches.length; i++) {
addToCacheList(CURRENT_TOUR.geocaches[i], false);
}
if (CURRENT_TOUR.geocaches.length <= 0) {
cacheList.html($.gctour.lang('tour.empty'));
}
handleResize();
var deleteButton = $('#gctourDeleteButton');
if (TOURS.length == 1 && deleteButton) {
deleteButton.hide();
} else {
deleteButton.show();
}
}
var addOpacityEffects = function(elem) {
$(elem)
.css({
opacity : "0.5"
})
.on({
mouseenter : function() {
$(this).stop().animate({
opacity : '1'
}, 200);
},
mouseleave : function() {
$(this).stop().animate({
opacity : '0.5'
}, 300);
}
});
};
function addClickEffect(element) {
return function() {
element.style.background = '#a9b2bf';
};
}
function removeClickEffect(element) {
return function() {
element.style.background = '#cdd8e8';
};
}
function addHoverEffect(element) {
return function() {
element.style.margin = '0px';
element.style.border = '1px solid lightgray';
element.style.background = '#cdd8e8';
};
}
function removeHoverEffect(element) {
return function() {
element.style.margin = '1px';
element.style.border = '0px solid lightgray';
element.style.background = '';
};
}
function addHoverEffects(element) {
element.addEventListener('mouseover', addHoverEffect(element), false);
element.addEventListener('mouseout', removeHoverEffect(element), false);
element.addEventListener('mousedown', addClickEffect(element), false);
element.addEventListener('mouseup', removeClickEffect(element), false);
element.style.margin = '1px';
}
async function getMapGeocache(gcid) {
try {
var mapCache,
additional_waypoints,
waypoint_i,
geocache = await getGeocache(gcid, 0);
if (geocache === "pm only" || geocache === 404) {
return geocache;
}
mapCache = {};
mapCache.gcid = geocache.gcid;
mapCache.guid = geocache.guid;
mapCache.image = geocache.image;
mapCache.name = geocache.name;
mapCache.difficulty = geocache.difficulty;
mapCache.terrain = geocache.terrain;
mapCache.latitude = geocache.lat;
mapCache.longitude = geocache.lon;
// save additional waypoints
additional_waypoints = geocache.additional_waypoints;
for (waypoint_i = 0; waypoint_i < additional_waypoints.length; waypoint_i++) {
additional_waypoints[waypoint_i].note = "";
}
mapCache.additional_waypoints = additional_waypoints;
return mapCache;
} catch(e) {
throw "fn getMapGeocache - " + e;
}
}
function getMapMarker(markerId) {
var position = getPositionOfId(markerId),
marker = CURRENT_TOUR.geocaches[position];
marker.index = position;
return marker;
}
function uploadMap(markerObj) {
markerObj = handleEmptyValuesForUpload(markerObj);
let jsonMap = JSON.stringify(markerObj).replace(/&/g, " and "); // IMPORTANT! prevents critical errors in webapplication
let headers = {'Content-type' : 'application/x-www-form-urlencoded'};
promiseRequest('POST', GCTOUR_HOST + 'map/save', headers, encodeURI("map=" + jsonMap));
}
async function makeMapFunction() {
try {
if (isEmptyTour(CURRENT_TOUR) || !isLoggedIn()) {
return;
}
// if tour has been uploaded, use "query tour" link on GCTour server instead of fetching data again
let $webcode = $('span#webcode:visible>b>a');
if ($webcode.length>0) {
$webcode[0].click();
return;
}
var gcIds = [],
wptIds = [],
allIds = [],
i;
// cache and custom marker IDs
for (i = 0; i < CURRENT_TOUR.geocaches.length; ++i) {
var marker = CURRENT_TOUR.geocaches[i];
if (marker.id) {
gcIds.push(marker.id);
allIds.push(marker.id);
} else if (marker.wptcode) {
wptIds.push(marker.wptcode);
allIds.push(marker.wptcode);
}
}
// add the overlay while loading
addProgressbar({
caption : $.gctour.lang('container.tourHeader.makeMap.wait'),
_document : document,
closeCallback : function(_document) {
return function() {
if (confirm($.gctour.lang('general.cancel') + "?") == true) {
window.location.reload();
}
};
}
});
// for progress bar update
PROGRESS_BAR.progress = 0;
PROGRESS_BAR.total = gcIds.length + wptIds.length;
// fetch all caches in parallel, not one by one
var promises = [];
for (i = 0; i < gcIds.length; i++) {
promises.push(getMapGeocache(gcIds[i]));
}
FAVSCORE = false; // fav score not needed for map preview
var cache_objects = await Promise.all(promises);
FAVSCORE = true;
// store map specific cache information in array
var geocaches = [];
for (i = 0; i < gcIds.length; i++) {
geocaches.push(cache_objects[i]);
}
// store custom markers in array
var costumMarkers = [];
for (i = 0; i < wptIds.length; i++) {
costumMarkers.push(getMapMarker(wptIds[i]));
updateProgressBar();
}
// store caches and custom markers in object
var cacheObject = {};
cacheObject.geocaches = geocaches;
cacheObject.costumMarkers = costumMarkers;
// upload map content to server
uploadMap(cacheObject);
// open map content from server in new tab (request hash value according to tour content)
var mapUrl = await getMapUrl(allIds.join(","));
setTimeout( function() { // ensure that GCTour server finished all tasks before opening map page
GM_openInTab(mapUrl + '#gui', false);
},1000);
closeOverlay();
} catch(e) {
addErrorDialog({
caption : "makeMapFunction error",
_exception : e
});
}
}
async function upload(tour, pmo, retracted) {
try {
if (!tour.password) {
tour.password = "not yet implemented";
}
tour = handleEmptyValuesForUpload(tour);
var jsonTour = JSON.stringify(tour).replace(/&/g, " and "); // IMPORTANT! prevents critical errors in web application
var headers = {'Content-type' : 'application/x-www-form-urlencoded'};
var response = await promiseRequest('POST', GCTOUR_HOST + 'tour/save', headers, encodeURI("tour=" + jsonTour));
var tourServer = JSON.parse(response.responseText);
// after an error you get this result, e.g.
// {"message":"wrong password","type":"error"}
// only if the result is a message
if (tourServer.message && tourServer.type == "error") {
var pw = prompt("falsches Passwort - bitte richtiges eingeben");
// if pw is empty or dialog closed
if (!pw) {
closeOverlay();
return;
}
tour.password = pw;
upload(tour, pmo, retracted);
} else if (tourServer.message && tourServer.type == "info") {
alert(tourServer.message);
closeOverlay();
} else { // result is a tour
// save password and webcode to tour
CURRENT_TOUR.password = tour.password;
CURRENT_TOUR.webcode = tourServer.webcode;
// save and update tour
saveTour(CURRENT_TOUR);
updateTour();
closeOverlay();
var str = $.gctour.lang('container.tourHeader.upload.tourUploaded1') + CURRENT_TOUR.webcode;
str += $.gctour.lang('container.tourHeader.upload.tourUploaded2').replace(/xWEBCODEx/g, CURRENT_TOUR.webcode);
str = '<div>' + str + '</div>';
showNotification({ text: str, style: "green", timeout: 20000 });
// Basic members cannot upload PMO caches
var nCaches = pmo.length;
if (nCaches > 0) {
var pmoc = $.gctour.lang('container.tourHeader.upload.tourUploadPMO') + ':';
for (let i = 0; i < nCaches; i++) {
pmoc += "<br>#" + pmo[i].index + ": " + pmo[i].name + " (" + pmo[i].id + ")";
}
pmoc = '<small><i>' + pmoc + '</i></small>';
showNotification({ text: pmoc, style: "yellow", timeout: 20000 });
}
// Retracted caches cannot be uploaded
nCaches = retracted.length;
if (nCaches > 0) {
var retr = $.gctour.lang('container.tourHeader.upload.tourUploadRetracted') + ':';
for (let i = 0; i < nCaches; i++) {
retr += "<br>#" + retracted[i].index + ": " + retracted[i].name + " (" + retracted[i].id + ")";
}
retr = '<small><i>' + retr + '</i></small>';
showNotification({ text: retr, style: "yellow", timeout: 20000 });
}
}
} catch(e) {
throw 'fn upload - ' + e;
}
}
async function uploadTourFunction(id) {
try {
if (isEmptyTour(CURRENT_TOUR) || !isLoggedIn()) {
return;
}
var i, tour, costumMarker, mapCache;
i = TOURS.map(function(tour) {
return tour.id
}).indexOf(id);
if (i == -1) {
throw ('uploadTourFunction(), line ' + new Error().lineNumber + ' - tour for index "' + id + '" not found');
}
tour = TOURS[i]; // tour object to upload
var nCaches = tour.geocaches.length;
// deep copy of tour to upload (otherwise tour changes unintentionally before saving the updated status)
CURRENT_TOUR = JSON.parse(JSON.stringify(TOURS[i]));
// add the overlay while loading
GM_setValue("stopTask", false);
addProgressbar({
_document : document,
closeCallback : function(_document) {
return function() {
if (confirm($.gctour.lang('general.cancel') + "?") == true) {
window.location.reload();
}
};
}
});
// for progress bar update
PROGRESS_BAR.progress = 0;
PROGRESS_BAR.total = nCaches;
// fetch all caches in parallel, not one by one
var promises = [];
for (i = 0; i < nCaches; i++) {
costumMarker = (typeof(tour.geocaches[i].latitude) != "undefined");
if (costumMarker) {
promises.push(tour.geocaches[i]);
updateProgressBar();
} else {
promises.push(getMapGeocache(tour.geocaches[i].id));
}
}
FAVSCORE = false; // fav score not needed for tour upload
var cache_objects = await Promise.all(promises);
FAVSCORE = true;
var geocaches = [], retracted = [], pmo = [], costumMarkers = [], gc;
var ind = 0; // in case that PMO caches are skipped for Basic Members or retracted caches are present
for (i = 0; i < nCaches; ++i) {
costumMarker = (typeof(tour.geocaches[i].latitude) != "undefined");
if (!costumMarker) {
mapCache = cache_objects[i];
if (mapCache === "pm only") {
gc = tour.geocaches[i];
gc.index = i + 1;
pmo.push(gc);
}
else if (mapCache === 404) {
gc = tour.geocaches[i];
gc.index = i + 1;
retracted.push(gc);
}
else {
geocaches.push(mapCache);
ind++;
}
} else {
var cm = cache_objects[i];
cm.index = ind;
ind++;
costumMarkers.push(cm);
}
}
// separate own waypoints and caches for tour upload
tour.costumMarkers = costumMarkers;
tour.geocaches = geocaches;
await upload(tour, pmo, retracted);
} catch (e) {
closeOverlay();
addErrorDialog({
caption : "uploadTourFunction error",
_exception : e
});
}
}
function isPremiumUser() {
try {
return (GS_USERINFO.userType === "Premium");
} catch(e) {
throw 'fn isPremiumUser - ' + e;
}
}
function switchToMyLists() {
(document.URL.search("plan/lists") >= 0) ? window.location.reload() : GM_openInTab(GS_HOST + 'plan/lists', false);
}
// prototype for bookmark lists
function Bookmarklist(id) {
this._id = id;
this._headers = { 'Content-Type': 'application/json', 'X-Verification-Token': TOKEN };
this._url = '';
}
// create list
Bookmarklist.prototype.create = async function() {
let list = {};
list.name = ('GCTour - ' + CURRENT_TOUR.name).replace(/%/g,'_'); // '%' in tour name causes error
list.description = 'GCTour';
list.lastUpdateUtc = (new Date()).toJSON();
list.count = 0;
list.type = { "code": "bm" };
list.isShared = true;
list.isPublic = false;
list.isNotify = false;
this._url = GS_WEB_API + 'lists';
let response = await promiseRequest("POST", this._url, this._headers, JSON.stringify(list));
let obj = JSON.parse(response.responseText);
// list id
this._id = obj.referenceCode;
}
// delete list
Bookmarklist.prototype.delete = function() {
this._url = GS_WEB_API + 'lists/' + this._id;
promiseRequest("DELETE", this._url, this._headers);
}
// add all caches to list
Bookmarklist.prototype.addGeocaches = async function(cacheArray) {
if (typeof cacheArray === "undefined") { // add all caches to bm list
cacheArray = [];
let geocaches = CURRENT_TOUR.geocaches;
let nCaches = Math.min(geocaches.length,1000); // max. 1000 caches per bookmark list
for (let i = 0; i < nCaches; i++) {
let cache = {};
cache.name = geocaches[i].name;
cache.id = geocaches[i].id;
cache.rownumber = i;
cache.referenceCode = cache.id;
cache.listItem = {};
cache.listItem.name = cache.name;
cache.listItem.description = "";
cacheArray.push(cache);
}
}
this._url = GS_WEB_API + 'lists/' + this._id + '/geocaches';
await promiseRequest("PUT", this._url, this._headers, JSON.stringify(cacheArray));
}
// delete caches from list (max. 500 per call)
Bookmarklist.prototype.deleteGeocaches = async function(cacheArray) {
if (typeof cacheArray === "undefined") { // delete all caches from list
cacheArray = [];
let geocaches = CURRENT_TOUR.geocaches;
let nCaches = geocaches.length;
for (let i = 0; i < nCaches; i++) {
cacheArray.push(geocaches[i].id);
}
}
this._url = GS_WEB_API + 'lists/' + this._id + '/geocaches/bulkdelete';
if (cacheArray.length>500) { // max. 500 per call
await promiseRequest("POST", this._url, this._headers, JSON.stringify(cacheArray.slice(0, 500)));
await promiseRequest("POST", this._url, this._headers, JSON.stringify(cacheArray.slice(500)));
}
else {
await promiseRequest("POST", this._url, this._headers, JSON.stringify(cacheArray));
}
}
// get all caches from list (max. 500 per call)
Bookmarklist.prototype.getGeocaches = async function() {
let skip = 0;
this._url = GS_WEB_API + 'lists/' + this._id + '/geocaches?skip=' + skip + '&take=500';
let response = await promiseRequest("GET", this._url, this._headers);
let arr = JSON.parse(response.responseText);
// if list has more than 500 caches, fetch the remaining ones
if (arr.total>500) {
skip = 500;
this._url = GS_WEB_API + 'lists/' + this._id + '/geocaches?skip=' + skip + '&take=500';
response = await promiseRequest("GET", this._url, this._headers);
arr.data = arr.data.concat(JSON.parse(response.responseText).data);
}
return arr;
}
// get metadata of list
Bookmarklist.prototype.getMetadata = async function() {
this._url = GS_WEB_API + 'lists/' + this._id;
let response = await promiseRequest("GET", this._url, this._headers);
return JSON.parse(response.responseText);
}
// synchronize name of list
Bookmarklist.prototype.synchronizeName = async function() {
// get metadata
var json = await this.getMetadata();
// if list name differs from tour name then update the list name
var targetName = 'GCTour - ' + CURRENT_TOUR.name;
if (json.name !== targetName) {
json.name = targetName;
this._url = GS_WEB_API + 'lists/' + this._id;
await promiseRequest("PUT", this._url, this._headers, JSON.stringify(json));
}
}
// add list link to tour header
Bookmarklist.prototype.addLink = function() {
$('span#webcode').after('<span id="gct_bml"><br>Bookmark: <b><a href="https://coord.info/' + this._id + '" target="_blank">' + this._id + '</a></b></span>');
// delete list and list link on click
let deleteImage = document.createElement('img');
deleteImage.alt = $.gctour.lang('general.delete');
deleteImage.title = $.gctour.lang('general.delete');
deleteImage.style.cursor = 'pointer';
deleteImage.style.width = '14px';
deleteImage.style.verticalAlign = 'text-bottom';
deleteImage.style.marginLeft = '5px';
deleteImage.src = $.gctour.img.del;
deleteImage.addEventListener('click', async function() {
await deleteBookmarklist(CURRENT_TOUR.bml);
$('span#gct_bml').remove(); // delete list link from tour header
}, true);
addOpacityEffects(deleteImage);
$('span#gct_bml').append(deleteImage);
}
async function saveAsBookmarklist() {
if (isEmptyTour(CURRENT_TOUR) || !isLoggedIn()) {
return;
}
// tour contains own waypoints only
if ($.grep(CURRENT_TOUR.geocaches, function(e) { return e.id; }).length === 0 && !CURRENT_TOUR.bml) { // number of caches
log('nothing to add to a bookmark list - tour contains own waypoints only');
return;
}
try {
// new lists page is only available for Premium member (Groundspeak decision)
if (!isPremiumUser()) {
switchToMyLists();
return;
}
addProgressbar({
caption: $.gctour.lang('container.tourHeader.saveAsBookmarklist.caption'),
closeCallback: function() {
return function() {
closeOverlayRemote(document)();
};
}
});
if (CURRENT_TOUR.bml) { // a bm list for this tour already exists --> update
try {
await updateBookmarklist(CURRENT_TOUR.bml);
} catch (e) { // bml id is invalid
closeOverlay();
// delete link to bml from tour and GUI
delete CURRENT_TOUR.bml;
$('span#gct_bml').remove();
// start over and create new bml
saveAsBookmarklist();
}
} else { // create new bm list
var bml = new Bookmarklist();
// create empty bookmark list
await bml.create();
// add caches from tour to bookmark list
try {
await bml.addGeocaches();
} catch (e) { // tour probably contains retracted caches
let mes = $.gctour.lang('container.tourHeader.upload.tourUploadRetracted') + ':<br>';
try {
let errorMessage = JSON.parse(JSON.parse(e).errorMessage).Message;
// index of retracted cache
let gccode = errorMessage.match(/GC[0-9a-zA-Z]{1,6}/)[0];
let nCaches = CURRENT_TOUR.geocaches.length;
let ind;
for (let i = 0; i < nCaches; i++) {
if (CURRENT_TOUR.geocaches[i].id === gccode) {
ind = i;
break;
}
}
mes += '"#' + (ind+1) + ": " + errorMessage + '"';
}
catch (e) {
mes += '"unknown GC code"';
}
mes = '<small><i>' + mes + '</i></small>';
showNotification({ text: mes, style: "yellow", timeout: 20000 });
}
closeOverlay();
// add link to bookmark list
bml.addLink();
// save BM link in tour
CURRENT_TOUR.bml = bml._id;
saveCurrentTour();
let str = '<div>' + $.gctour.lang('container.tourHeader.saveAsBookmarklist.success') + ':<br><a href="https://coord.info/' + bml._id + '" target="_blank">https://coord.info/' + bml._id + '</a></div>';
showNotification({ text: str, style: "green" });
}
} catch (e) {
addErrorDialog({
caption : "saveAsBookmarklist error",
_exception : e
});
switchToMyLists();
}
}
/* update bm list s.t. it matches the current tour:
* - synchronize tour name to bm list name
* - cache in tour but not in bm list, i.e. cache added to tour --> add cache to bm list
* - cache in bm list but not in tour, i.e. cache removed from tour --> delete cache from bm list
*/
async function updateBookmarklist(bmId) {
// synchronize tour name to bm list name
let bml = new Bookmarklist(bmId);
await bml.synchronizeName();
// get all cache ids from current tour
let tourIDs = [];
let nCaches = CURRENT_TOUR.geocaches.length;
for (let i = 0; i < nCaches; i++) {
if (CURRENT_TOUR.geocaches[i].id) { // only geocaches, no own marker
tourIDs.push(CURRENT_TOUR.geocaches[i].id);
}
}
// get all caches from bm list
var gcs = await bml.getGeocaches();
gcs = gcs.data;
let bmlIDs = [];
for (let i=0; i<gcs.length; i++) {
bmlIDs.push(gcs[i].referenceCode);
}
// caches added to tour --> add to bml
let missingInBml = tourIDs.filter(x => !bmlIDs.includes(x));
// caches removed from tour --> remove from bml
let missingInTour = bmlIDs.filter(x => !tourIDs.includes(x));
// caches that were added to tour also get added to bm list
if (missingInBml.length>0) {
var cacheArray = [];
for (let i = 0; i < missingInBml.length; i++) {
let cache = {};
let ind = CURRENT_TOUR.geocaches.findIndex(cache => cache.id === missingInBml[i]); // index in current tour
cache.name = CURRENT_TOUR.geocaches[ind].name;
cache.id = CURRENT_TOUR.geocaches[ind].id;
cache.rownumber = gcs.length+i; // add row to end of bm list
cache.referenceCode = cache.id;
cache.listItem = {};
cache.listItem.name = cache.name;
cache.listItem.description = "";
cacheArray.push(cache);
}
try {
await bml.addGeocaches(cacheArray);
} catch (e) { // tour probably contains retracted caches
let mes = $.gctour.lang('container.tourHeader.upload.tourUploadRetracted') + ':<br>';
try {
let errorMessage = JSON.parse(JSON.parse(e).errorMessage).Message;
// remove retratced GC code from missing in BM message
let gccode = errorMessage.match(/GC[0-9a-zA-Z]{1,6}/)[0];
missingInBml = missingInBml.filter(item => item !== gccode);
// index of retracted cache
let i = tourIDs.indexOf(gccode);
mes += '"#' + (i+1) + ": " + errorMessage + '"';
}
catch (e) {
mes += '"unknown GC code"';
}
mes = '<small><i>' + mes + '</i></small>';
showNotification({ text: mes, style: "yellow", timeout: 20000 });
}
}
// caches that were deleted from tour also get deleted from bm list
if (missingInTour.length>0) {
await bml.deleteGeocaches(missingInTour);
}
closeOverlay();
// show info dialog
let template = '<a href="https://coord.info/GCxxx" target="_blank">GCxxx</a>';
let missingInBml_links = missingInBml.map(x => template.replace(/GCxxx/g,x)).join(', ');
let missingInTour_links = missingInTour.map(x => template.replace(/GCxxx/g,x)).join(', ');
let message = '<div>'+$.gctour.lang('container.tourHeader.saveAsBookmarklist.up-to-date')+':<br><a href="https://coord.info/'+bmId+'" target="_blank">https://coord.info/'+bmId+'</a><br>';
message += '<br>'+$.gctour.lang('container.tourHeader.saveAsBookmarklist.added')+' ('+missingInBml.length+'):<br>'+missingInBml_links;
if (missingInBml.length>0) message += '<br>';
message += '<br>'+$.gctour.lang('container.tourHeader.saveAsBookmarklist.removed')+' ('+missingInTour.length+'):<br>'+missingInTour_links;
message += '<br><br></div>';
showNotification({ text: message, style: "green", timeout: 10000 });
}
async function deleteBookmarklist(bml_id) {
// delete bookmark list
let bml = new Bookmarklist(bml_id);
bml.delete();
// delete bookmark list id from tour
delete CURRENT_TOUR.bml;
saveCurrentTour();
}
function openGarminExpress(url) {
// if installed then open, otherwise return false
try {
var $iframe = $("<iframe />");
$iframe.css({
"display": "none"
});
$iframe.appendTo("body");
$iframe[0].contentWindow.location.href = url;
return true;
} catch (e) {
return false;
}
}
async function send2Garmin() {
if (isEmptyTour(CURRENT_TOUR) || !isLoggedIn()) {
return;
}
try {
addProgressbar();
var url = GS_WEB_API + 'garminexpress',
headers = { 'Content-Type': 'application/json', 'X-Verification-Token': TOKEN },
isPM = isPremiumUser(),
caches = [],
promises = [],
i;
// only consider geocaches, ignore own waypoints
for (i=0; i<CURRENT_TOUR.geocaches.length; i++) {
if (typeof(CURRENT_TOUR.geocaches[i].id) !== "undefined") {
caches.push(CURRENT_TOUR.geocaches[i]);
// for BM, prepare to check if cache is PMO
if (!isPM) {
let url2 = GS_WEB_API + 'Geocache/'+CURRENT_TOUR.geocaches[i].id;
promises.push(promiseRequest("GET", url2, headers));
}
}
}
// for BM exclude PMO caches (if 1 PMO cache would be present for BM then nothing gets transferred)
if (!isPM) {
let cache_objects = await Promise.all(promises);
for (i=0; i<cache_objects.length; i++) {
let resp = JSON.parse(cache_objects[i].responseText);
if (resp.state.isPremiumOnly) {
caches.splice(i,1);
}
}
}
// no caches left to transfer
if (caches.length===0) {
closeOverlay();
return;
}
var cacheIDArray = [];
for (i=0; i<caches.length; i++) {
cacheIDArray.push( gCCode2ID(caches[i].id.toUpperCase()) );
}
var data = {"geocacheIds": cacheIDArray, "name": CURRENT_TOUR.name, "referenceCode": "GCTour"};
// upload request for Garmin Device data
var response = await promiseRequest("POST", url, headers, JSON.stringify(data));
var hash = JSON.parse(response.responseText);
// call Garmin Express locally
url = 'garminexpress://manifestupdate/?manifesturl=https://api.groundspeak.com/web/v1/garminexpress/public/manifest/' + hash;
if (!openGarminExpress(url)) { // if Garmin Express is not available show info dialog
var info =
'<div style="text-align:center;">' +
'<p style="text-align:left;">Groundspeak message:</p>' +
' <b><i>Garmin Express not detected</b>' +
' <p>Make sure you have the latest version of Garmin Express installed.</p>' +
' <p><a href="http://software.garmin.com/en-US/express.html" target="_blank">Get Garmin Express</a></p></i>' +
'</div>';
showNotification({ text: info, style: "red", timeout: 10000 });
}
closeOverlay();
} catch (e) {
addErrorDialog({
caption : "Send to Garmin error",
_exception : e
});
}
}
function openSettingsDialog() {
// open only once
if ($("#settings\\.gpx\\.wpts").length > 0) return;
log_timeStart('show settings menu');
var settings = new Settings_jqUI();
settings.show();
// fix select css on cache pages
$('div#tabs select').css('background-image', 'none');
// indent special gpx options
const special_options = "#settings\\.gpx\\.ignoreParkWpts, #settings\\.gpx\\.ignoreEqualListingCoordsWpts, #settings\\.gpx\\.wptAsGeocache"
$(special_options).parent().css('padding-left', '20px');
// toggle disable/enable of special gpx options
$("#settings\\.gpx\\.wpts").on('change', function() {
if (GM_getValue('gpxwpts', true)) {
$(special_options).parent().css('color', '');
$(special_options).prop('disabled', false);
} else {
$(special_options).parent().css('color', 'darkgray');
$(special_options).prop('disabled', true);
}
});
// trigger on settings load
$("#settings\\.gpx\\.wpts").trigger('change');
log_timeEnd('show settings menu');
}
function populateTours() {
var tourIt,
tour,
$tourListLi,
$tourLink,
$webImage,
$deleteButton;
var $tourList = $('#dialogListContainer');
$tourList.html("");
var $tourListUl = $('<ul>', {
"class" : "dialogList",
"id" : "sortable"
});
$tourList.append($tourListUl);
// construct tour list
for (tourIt = 0; tourIt < TOURS.length; tourIt++) {
tour = TOURS[tourIt];
$tourListLi = $('<li>', {
id : "tour" + tour.id
});
$tourListUl.append($tourListLi);
$tourLink = $('<a>', {
"css" : {
"cursor" : "pointer",
"font-size" : "10px",
"color" : "#003399"
},
html : tour.name + "&nbsp;<small>(" + tour.geocaches.length + ")</small>"
})
.on('click', {
tour : tour
}, function (e) {
showCacheList(e.data.tour)();
});
if (tour.webcode) {
$webImage = $('<img>', {
src : $.gctour.img.globe,
"css" : {
"float": "left",
"margin-right" : 3
}
});
$tourLink.prepend($webImage);
}
if (tour.id == CURRENT_TOUR.id) {
$tourLink.css({
'font-weight' : 'bolder'
});
} else {
$deleteButton = $('<img>', {
title : $.gctour.lang('tour.delete'),
src : $.gctour.img.del,
"css" : {
"cursor" : 'pointer',
"float" : 'right'
}
})
.on('click', {
tour : tour
}, function(e) {
deleteTour(e.data.tour.id)();
});
$tourLink.append($deleteButton);
}
$tourListLi.prepend($tourLink);
}
// sorting tours by drag and drop
$tourListUl.sortable({
axis : "y",
cursor: "move",
helper: "clone", // prevent to fire click events
items: "> li",
placeholder : "gct_sortable-placeholder",
revert: true,
start : function(e, ui) {
// save old position
$(this).data('old-pos', ui.item.index());
},
update: function(e, ui) {
var oldPos = $(this).data('old-pos');
var newPos = ui.item.index();
debug("Drag n Drop in progress:\n" +
"\tMove tour " + TOURS[oldPos].name + " (id: " + ui.item.attr('id') + ") from '" + oldPos + "' to '" + newPos + "'");
// positions to insert/remove
var insertPos = (oldPos > newPos) ? newPos : newPos + 1;
var removePos = (oldPos < newPos) ? oldPos : oldPos + 1;
// insert tour to new position, then remove tour from old position
TOURS.splice(insertPos, 0, TOURS[oldPos]);
TOURS.splice(removePos, 1);
// save tour with new order
saveValue('tours', TOURS);
},
stop : function() {
// remove old position
$(this).removeData('old-pos');
}
});
}
function showCacheList(tour) {
return function() {
var cacheList = document.getElementById('dialogDetails');
cacheList.scrollTop = 0;
cacheList.setAttribute("tourid", tour.id);
cacheList.innerHTML = "<u><b>" + tour.name + "</b>";
if (tour.webcode) {
cacheList.innerHTML += "&nbsp;&nbsp;&nbsp;<i>Webcode: <a href='" + GCTOUR_HOST + "tour/" + tour.webcode.trim() + "' title='" + $.gctour.lang('container.tourHeader.makeMap.caption') + "' target='_blank'>" + tour.webcode + "</a></i>";
}
cacheList.innerHTML += "</u><br/>";
var copyButton = document.createElement('img');
copyButton.title = $.gctour.lang('tour.copy');
copyButton.src = $.gctour.img.copy;
copyButton.style.cursor = 'pointer';
copyButton.style.marginRight = '5px';
copyButton.style.cssFloat = 'right';
copyButton.addEventListener('click', function() {
var newTour = JSON.parse(JSON.stringify(tour));
newTour.id = getNewTourId();
newTour.name = newTour.name + " - " + $.gctour.lang('tour.copyNameAppendix');
TOURS.push(newTour);
log("Creating copy tour: " + newTour.id + " ; " + newTour.name);
saveTour(newTour, true);
populateTours();
showCacheList(newTour)();
}, false);
var deleteButton = document.createElement('img');
deleteButton.title = $.gctour.lang('tour.delete');
deleteButton.src = $.gctour.img.del;
deleteButton.style.cursor = 'pointer';
deleteButton.style.marginRight = '5px';
deleteButton.style.cssFloat = 'right';
deleteButton.addEventListener('click', deleteTour(tour.id), false);
var renameButton = document.createElement('img');
renameButton.src = $.gctour.img.edit;
renameButton.title = $.gctour.lang('general.rename');
renameButton.alt = $.gctour.lang('general.rename');
renameButton.style.cursor = 'pointer';
renameButton.style.marginRight = '5px';
renameButton.style.cssFloat = 'right';
renameButton.addEventListener('click',
function() {
var newTourName = prompt($.gctour.lang('tour.newDialog'), tour.name);
if (!newTourName) {
return;
}
tour.name = newTourName;
saveTour(tour, true);
populateTours();
showCacheList(tour)();
}, false);
if (tour.id != CURRENT_TOUR.id) {
cacheList.insertBefore(deleteButton, cacheList.firstChild);
}
cacheList.insertBefore(renameButton, cacheList.firstChild);
cacheList.insertBefore(copyButton, cacheList.firstChild);
var cacheListUl = createElement('ul');
cacheListUl.setAttribute("class", "dialogList");
for (var cacheIt = 0; cacheIt < tour.geocaches.length; cacheIt++) {
var geocache = tour.geocaches[cacheIt];
var cacheListLi = createElement('li');
append(cacheListLi, cacheListUl);
cacheListLi.innerHTML = "<img src='" + geocache.image + "' style='margin-left=10px'> " + geocache.name + '&nbsp;<small style="font-size: 90%;">' + ((geocache.id !== undefined) ? '('+geocache.id+')' : '') + "</small>";
}
append(cacheListUl, cacheList);
// make loadButton available
var loadButton = document.getElementById('loadButton');
loadButton.value = '"' + tour.name + '"';
loadButton.removeAttribute('disabled');
// first remove all active tour css classes
$("ul.dialogList > li").removeClass("activeTour");
// and then set it to the clicked tour
$('#tour' + tour.id).addClass("activeTour");
};
}
function moveEntryToOtherTour(entryId,tourId) {
// get entry from current tour
var pos = getPositionOfId(entryId);
var entry = CURRENT_TOUR.geocaches[pos];
// get other tour
var otherTour = getTourById(tourId);
// check if element is already in other tour
var isInTour = false;
$.each(otherTour.geocaches, function(i, obj) {
if (obj.id == entryId || obj.wptcode == entryId) {
isInTour = true;
return false; // break each
}
});
if (isInTour) {
showGeocacheNotification({
id : (entry.id || entry.name),
name : entry.name,
image : entry.image,
tourname : otherTour.name
}, {
type : "contains"
});
return;
}
debug("moving '" + entryId + "' to '"+ otherTour.name + "'");
// add entry to other tour
otherTour.geocaches.push(entry);
// save modified other tour without changing current tour
saveTour(otherTour, true);
// delete entry from current tour and update
deleteElement(entryId)();
showGeocacheNotification({
id : (entry.id || entry.name),
name : entry.name,
image : entry.image,
tourname : otherTour.name
}, {
type : "success"
});
}
function openTourDialog(entryId) {
var overLay = getOverlay({
caption : (entryId === undefined) ? $.gctour.lang('container.toolbar.openTour') : $.gctour.lang('container.cacheList.moveToList')
});
// make dialog draggable
$('#dialogBody').draggable({
cancel: '#dialogListContainer,#dialogDetails,input',
// cursor: "move", // does not work
start: function() {
$(this).css('cursor', 'move');
},
stop: function() {
$(this).css('cursor', 'default');
}
});
var tourList = createElement('div', {
id : "dialogListContainer"
});
append(tourList, overLay);
var cacheList = createElement('div', {
id : "dialogDetails"
});
append(cacheList, overLay);
populateTours();
// load,close buttons
var buttonsDiv = createElement('div', {
style : "width:580px;position: absolute; bottom: 10px;"
});
append(buttonsDiv, overLay);
buttonsDiv.setAttribute('class', 'dialogFooter');
var closeButton = createElement('input', {
type : "button",
value : $.gctour.lang('general.cancel'),
style : "background-image:url(" + $.gctour.img.closebutton + ")"
});
append(closeButton, buttonsDiv);
closeButton.addEventListener('click', closeOverlay, false);
var loadButton = createElement('input', {
type : "button",
value : $.gctour.lang('general.load'),
disabled : "",
id : "loadButton",
style : "background-image:url(" + $.gctour.img.openTour + ")"
});
append(loadButton, buttonsDiv);
loadButton.addEventListener('click', function() {
var id = $("#dialogDetails").attr("tourid");
if (entryId === undefined) {
loadTour(id)();
} else {
moveEntryToOtherTour(entryId,id);
}
closeOverlay();
}, false);
// load currentTour
showCacheList(CURRENT_TOUR)();
loadButton.setAttribute("disabled", "disabled");
}
async function downloadTour(webcode, _url) {
try {
var url,
onlineTour;
// add the overlay while loading
addProgressbar();
url = GCTOUR_HOST + 'tour/' + webcode.trim() + '/json';
if (typeof _url !== "undefined") { // url to old server
url = _url;
}
var response = await promiseRequest('GET', url),
booResponse = (response.status === 200), // only status 200
booIsJson = isJSON(response.responseText); // is response json ?
if (!booResponse || !booIsJson) {
alert("Webcode '" + webcode + "' could not be loaded.\n" +
response.status + ", " + response.statusText + ((booIsJson) ? "" : ", format is not valid"));
closeOverlay();
return false;
}
var responseObject = JSON.parse(response.responseText);
if (responseObject.type == "error" && responseObject.message == "no tour") {
// webcode not found on server
closeOverlay();
let str = '<div>' + $.gctour.lang('container.toolbar.downloadTour.webcodeerror') + '</div>';
showNotification({ text: str, style: "yellow" });
} else if (responseObject.type == "oldtour") {
onlineTour = JSON.parse(responseObject.message);
onlineTour.id = getNewTourId();
TOURS.push(onlineTour);
saveCurrentTour();
log("Download of an old online tour successful: " + onlineTour.id + " ; " + onlineTour.name);
closeOverlay();
alert("tour '" + onlineTour.name + "'\n" + $.gctour.lang('container.toolbar.downloadTour.webcodesuccess') + "\n" + $.gctour.lang('container.toolbar.downloadTour.webcodeOld'));
loadTour(onlineTour.id)();
} else {
onlineTour = responseObject;
// sanitize cache names
for (var i = 0; i < onlineTour.geocaches.length; i++) {
onlineTour.geocaches[i].name = escapeHTML(onlineTour.geocaches[i].name);
}
let index = TOURS.findIndex(tour => tour.webcode === onlineTour.webcode);
let id, tour;
if (index === -1) {
// create new tour
onlineTour.id = getNewTourId();
TOURS.push(onlineTour);
id = onlineTour.id;
tour = onlineTour;
} else {
// update existing tour
TOURS[index].geocaches = onlineTour.geocaches;
id = TOURS[index].id;
tour = TOURS[index];
}
saveTour(tour);
closeOverlay();
var str = "<div><br>Tour <u>" + onlineTour.name + "</u> " + $.gctour.lang('container.toolbar.downloadTour.webcodesuccess') + "</div>";
showNotification({ text: str, style: "green" });
loadTour(id)();
}
} catch (e) {
addErrorDialog({
caption : "Download tour error",
_exception : e
});
}
}
function downloadTourDialog() {
var overlay = getOverlay({
caption : $.gctour.lang('container.toolbar.downloadTour.caption'),
minimized : true
});
var divEbene = createElement('div');
append(divEbene, overlay);
divEbene.innerHTML = '<b>Webcode:</b>&nbsp;&nbsp;&nbsp;&nbsp;<input type="text" id="webcodeInput" class="gctour-input-text" style="width:300px;"><br/>' + $.gctour.lang('container.toolbar.downloadTour.webcodeDownloadHelp');
divEbene = createElement('div');
append(divEbene, overlay);
divEbene.setAttribute('class', 'dialogFooter');
var downloadButton = createElement('input', {
type : "button",
value : $.gctour.lang('container.toolbar.downloadTour.caption'),
style : "background-image:url(" + $.gctour.img.download + ")"
});
append(downloadButton, divEbene);
downloadButton.addEventListener('click', function() {
var webcode = $('#webcodeInput').val().trim();
if (webcode == "") {
return;
}
downloadTour(webcode);
}, false);
}
/* send2cgeo */
function send2cgeo() {
var caches = CURRENT_TOUR.geocaches,
cacheIDsCount = 0, // number of caches
group = 1, // number of IDs to submit simultaneously
url = "https://send2.cgeo.org/add.html",
$pBar = $("#gctour_send2cgeo_progressbar"),
$btn = $("#btnSend2cgeo"),
txtReg = "Register first!", // response from Send2cgeo if browser is not registered
txtSuc = "Success!", // response from Send2cgeo if submission was successful
// cache IDs
cacheIDs = $.map(caches, function(n) {
return ((n.id !== undefined) ? n.id : null);
});
cacheIDsCount = cacheIDs.length;
// set progress bar options
$pBar
.progressbar("option", {
"value" : 0,
"max" : cacheIDsCount
})
.removeClass("hide")
.css({ // necessary for pages in new design (search, dashboard,...), otherwise the progress bar doesn't show up
opacity : "",
display : "",
position : "",
height : "",
width : "",
top : "",
left : "",
fontSize: ""
});
// disable Send2cgeo button
$btn.button("disable");
// send cacheIDs recursively
async function sendRequests(fromPos) {
group = cacheIDsCount; // instead one by one, send all cacheIDs by one call
var toPos = fromPos + group,
param,
res,
boo = true;
toPos = (toPos > cacheIDsCount) ? cacheIDsCount : toPos;
// cacheIDs to send
param = cacheIDs.slice(fromPos, toPos).join(",");
// submit and wait for response
res = await promiseRequest('GET', url + "?cache=" + param); // https://send2.cgeo.org/add.html?cache=GC123,GC345
res = res.responseText;
$pBar.progressbar("option", "value", toPos);
if (res.indexOf(txtReg) !== -1) { // browser not registered
boo = false;
showNotification({
text: $.gctour.lang('send2cgeo.register'),
style: "yellow",
timeout: 10000
});
$pBar.addClass("hide");
$btn.button("enable");
} else if (res.indexOf(txtSuc) === -1) { // response not ok (cache probably could not be added to the queue)
boo = false;
showNotification({
text: $.gctour.lang('send2cgeo.senderror'),
style: "red",
timeout: 10000
});
$pBar.addClass("hide");
$btn.button("enable");
}
fromPos = toPos;
if (cacheIDsCount > fromPos && boo) {
sendRequests(fromPos);
}
}
// start recursion by sending first cacheID
if (cacheIDsCount > 0) {
sendRequests(0);
}
}
var openGcTour2cgeoDialog = function() {
if (isEmptyTour(CURRENT_TOUR) || !isLoggedIn()) {
return;
}
var cacheIDsCount = $.grep(CURRENT_TOUR.geocaches, function(n) {
return (n.id !== undefined);
}).length,
waypointCount = CURRENT_TOUR.geocaches.length - cacheIDsCount,
noWP = (waypointCount > 0 ? ' (' + $.gctour.lang('send2cgeo.noWP') + ')' : ''),
btnText = $.gctour.lang('send2cgeo.title') + noWP,
send =
'<div class="gct_send2cgeo">' +
'<br>' +
'<ol>' +
'<li>' +
'<div><button id="btnSend2cgeo">' + btnText + '</button></div>' +
'<div id="gctour_send2cgeo_progressbar" class="hide">' + // hide progress bar
'<div class="progress-label"></div>' +
'</div>'+
'</li>' +
$.gctour.lang('send2cgeo.usage') +
'</ol>' +
'</div>',
setup =
'<div class="gct_send2cgeo">' +
'<strong>' + $.gctour.lang('send2cgeo.setup.header') + '</strong>' +
'<ol>' +
'<li><a href="https://send2.cgeo.org/api/browser.html" target="_blank" title="https://send2.cgeo.org/api/browser.html">' + $.gctour.lang('send2cgeo.setup.registerBrowser') + '</a></li>' +
$.gctour.lang('send2cgeo.setup.desc1') +
'<li>' +
'<form name="device" action="https://send2.cgeo.org/pin.html" target="_blank" method="post">' +
'<label for="pin" class="gctour-label">' + $.gctour.lang('send2cgeo.setup.desc2') + '</label> ' +
'<input name="pin" type="text" placeholder="12345" maxlength="5" class="gctour-input-text" style="width: 5em; text-align: center;"/>' +
'<input name="button" type="submit" value="' + $.gctour.lang('send2cgeo.setup.desc3') + '" class="ui-button" style="margin-left: 10px;"/>' +
'</form>' +
'</li>' +
'</ol>' +
'</div>' +
'<br>' +
'<hr>' +
'<div class="gct_send2cgeo">' +
'<strong>Send2cgeo</strong>' +
'<ul>' +
'<li><a target="_blank" href="https://send2.cgeo.org/home.html" title="https://send2.cgeo.org/home.html">' + $.gctour.lang('send2cgeo.overview') + '</a></li>' +
'<li><a target="_blank" href="http://www.cgeo.org/faq.html#send2cgeo" title="http://www.cgeo.org/faq.html#send2cgeo">FAQ</a></li>' +
'</ul>' +
'</div>',
// dialog tabs structure
$tabs = $(
'<div id="tabs">' +
' <ul>' +
' <li><a href="#tabs-1">Send</a></li>' +
' <li><a href="#tabs-2">Setup</a></li>' +
' </ul>' +
' <div id="tabs-1">' + send + '</div>' +
' <div id="tabs-2">' + setup + '</div>' +
'</div>'
);
$tabs.on("dialogcreate", function() {
var $thisDlg = $(this).dialog("widget"),
$progressbar = $thisDlg.find("#gctour_send2cgeo_progressbar"),
$pl = $thisDlg.find(".progress-label");
$thisDlg.find("input[type=submit], button").button();
$progressbar.progressbar({
value: false,
max: CURRENT_TOUR.geocaches.length,
change: function() {
var $pBar = $(this),
value = $pBar.progressbar("value"),
max = $pBar.progressbar("option", "max");
$pl.text(value + " / " + max);
},
complete: function() {
$pl.text($pl.text() + " Complete!");
}
});
$thisDlg.find("#btnSend2cgeo").on('click', {}, function() {
send2cgeo();
});
});
$tabs.on("dialogclose", function() {
$(this).dialog("destroy");
});
// tabs menu
$tabs.dialog($.gctour.dialog.info(), {
title: $.gctour.lang('send2cgeo.title'),
width: '75%',
maxHeight: $(window).height() - 20,
resizable: false,
dialogClass: 'gct_dialog',
buttons: [{
text: $.gctour.lang('general.close'),
icons: {
primary: "ui-icon-closethick"
},
click: function() {
$(this).dialog("close");
}
}]
});
$("div#tabs").tabs({
heightStyle: "content"
});
// Increase z-index, otherwise dialog is not clickable
$('#tabs').parent().css('z-index', 9999);
};
/* overlays */
/* usage:
var options = {
caption: "Test Beschriftung",
color: 'red',
_document: document,
minimized: true,
closeCallback : function(_document){alert('oioio');}
}
getOverlay(options);
*/
function getOverlay(options) {
var bodyNew,
closeButton,
caption,
localDocument,
background_color;
caption = options.caption;
localDocument = options._document || document;
background_color = options.color || "#B2D4F3";
bodyNew = localDocument.getElementsByTagName('body')[0];
// first - close all old overlays
closeOverlayRemote(localDocument)();
var overLay = localDocument.createElement('div');
overLay.align = 'center';
overLay.className = 'dialogMask';
overLay.id = "dialogMask";
var dialogBody = localDocument.createElement('div');
dialogBody.id = "dialogBody";
dialogBody.className = "dialogBody header";
if (options.minimized) {
dialogBody.className += " dialogMin";
}
var dialogHead = localDocument.createElement('h1');
append(dialogHead, dialogBody);
dialogHead.style.backgroundColor = background_color;
var icon = "<img style='float:left;position:relative;top:-3px;' src='" + $.gctour.img.gctourLogo + "'>";
dialogHead.innerHTML = icon + caption;
closeButton = createElement('img', {
style : "cursor:pointer;"
});
append(closeButton, dialogHead);
closeButton.style.cssFloat = "right";
closeButton.src = $.gctour.img.closebutton;
var closeFunction = options.closeCallback || closeOverlayRemote;
closeButton.addEventListener('click', closeFunction(localDocument), false);
// close with ESC key
$(document).on('keydown', function(e) {
if (e.keyCode === 27) { // ESC
closeFunction(localDocument)();
}
});
var dialogContent = localDocument.createElement('div');
append(dialogContent, dialogBody);
dialogContent.className = "dialogContent";
bodyNew.appendChild(overLay);
bodyNew.appendChild(dialogBody);
// make overlay draggable
$("#dialogBody").draggable();
return dialogContent;
}
function closeOverlayRemote(theDocument) {
return function() {
$(theDocument)
.find("#dialogMask").remove().end()
.find("#dialogBody").remove().end()
.find("#progressOverlay").remove();
};
}
function closeOverlay() {
closeOverlayRemote(document)();
}
function getListOverlay(options) {
var overlay = getOverlay(options);
var list = createElement('div', {
id : "dialogListContainer"
});
append(list, overlay);
var listUl = createElement('ul');
listUl.setAttribute("class", "dialogList");
append(listUl, list);
var details = createElement('div', {
id : "dialogDetails"
});
append(details, overlay);
var dialogFooter = createElement('div', {
style : "width:580px;position: absolute; bottom: 10px;"
});
append(dialogFooter, overlay);
dialogFooter.setAttribute('class', 'dialogFooter');
var close = createElement('input', {
type : "button",
value : $.gctour.lang('general.close'),
style : "background-image:url(" + $.gctour.img.save + ")"
});
append(close, dialogFooter);
close.addEventListener('click', closeOverlay, false);
return [listUl, details];
}
function addErrorDialog(options) {
var localDocument;
localDocument = options._document || document;
closeOverlay();
options.minimized = true;
options.color = "#f00";
options.caption = options.caption || 'Error';
// log the exception
log_exception(options._exception);
var overlay = getOverlay(options);
$(overlay).append(
$('<div/>')
.css('border', '1px dashed red')
.css('clear', 'both')
.css('margin', '3px').css('padding', '5px')
.html('<b>' + options._exception + '</b>'),
$('<div/>')
.html($.gctour.lang('dlg.error.content')),
$('<div/>')
.addClass('dialogFooter')
.append(
$('<input/>')
.attr('onclick', 'return false;')
.attr('type', 'button')
.attr('value', $.gctour.lang('general.close'))
.css('background-image', 'url(' + $.gctour.img.closebutton + ')')
.on('click', function() {
if (localDocument == document) {
closeOverlayRemote(localDocument)();
} else { // if we are on the printview - close the whole window
localDocument.defaultView.close();
}
})
)).find("#gctour_update_error_dialog").on('click', function() {
update(true);
});
}
function addProgressbar(options) {
var overlay;
if (options) {
var theDocument = options._document || document;
var theCaption = options.caption || $.gctour.lang('general.pleaseWait');
if (options.closeCallback) {
overlay = getOverlay({
caption : theCaption,
minimized : true,
_document : theDocument,
closeCallback : options.closeCallback
});
} else {
overlay = getOverlay({
caption : theCaption,
minimized : true,
_document : theDocument
});
}
} else {
overlay = getOverlay({
caption : $.gctour.lang('general.pleaseWait'),
minimized : true,
_document : document
});
}
var progressBarContainer = document.createElement('div');
append(progressBarContainer, overlay);
progressBarContainer.style.marginLeft = "135px";
var progressBar = document.createElement('div');
append(progressBar, progressBarContainer);
progressBar.style.border = '1px solid lightgray';
progressBar.style.height = '13px';
progressBar.style.width = '208px';
progressBar.style.cssFloat = 'left';
progressBar.style.margin = '10px';
progressBar.style.align = 'center';
progressBar.style.lineHeight = '13px';
progressBar.style.verticalAlign = 'middle';
progressBar.style.background = "url(" + $.gctour.img.loader2 + ")";
progressBar.style.setProperty("-moz-border-radius", "4px", "");
progressBar.style.setProperty("border-radius", "4px", "");
var progressBarElement = document.createElement('div');
append(progressBarElement, progressBarContainer);
progressBarElement.id = 'progressbar';
progressBarElement.style.opacity = '0.6';
progressBarElement.style.width = '0px';
progressBarElement.style.height = '13px';
progressBarElement.style.fontSize = '10px';
progressBarElement.style.backgroundColor = '#E78F08';
progressBarElement.style.position = 'absolute';
progressBarElement.style.margin = '11px';
progressBarElement.align = 'center';
progressBarElement.style.setProperty("-moz-border-radius", "4px", "");
progressBarElement.style.setProperty("border-radius", "4px", "");
}
function setProgress(i, count, theDocument) {
var width = ((208 * (i + 1)) / count);
$("#progressbar", theDocument)
.css('width', width)
.html("<b>" + (i + 1) + "/" + count + "</b>");
}
function updateProgressBar() {
setProgress(PROGRESS_BAR.progress, PROGRESS_BAR.total, document);
PROGRESS_BAR.progress += 1;
}
/* gui notifications */
$.gctour.notification = $.gctour.notification || {};
$.gctour.notification.init = function() {
$('<ul>', {
id : "gctour-notification-box"
}).appendTo('body');
};
$.gctour.notification.add = function(options) {
var content = options.icon ? "<img style='float:left;padding-right:6px;' src='" + options.icon + "'/>" : "";
content += options.title ? "<span style='font-size:18px'><b>" + options.title + "</b></span><br/>" : "";
content += options.text ? options.text : "";
var timeout = options.timeout ? options.timeout : 6000;
var $note = $('<li>', {
"class" : "gctour-notification-" + (options.style ? options.style : "green")
})
.on('click', function () {
$(this).animate({
height: 0
}, 600, "linear", function () {
$(this).remove();
})
})
//.disableSelection()
.append(
$('<div>', {
html : content,
css : {
'font-size' : '13px',
'line-height' : '16px',
'padding' : '8px 10px 9px',
'position' : 'relative',
'text-align' : 'left',
'width' : 'auto'
}
})
)
.prependTo($('#gctour-notification-box'));
setTimeout(function() {
$note.animate({
height : 0
}, 600, "linear", function() {
$note.remove();
});
}, timeout);
};
$.gctour.notification.test = function() {
if (DEBUG_MODE) {
var dummyNote = [{
title: "test 1",
icon: GS_HOST + GS_WPT_IMAGE_PATH + "2.gif",
text: "test notification",
style: "yellow"
}, {
title: "test 2",
icon: GS_HOST + GS_WPT_IMAGE_PATH + "3.gif",
text: "test notification, longer text",
style: "red"
}, {
title: "test 3",
icon: GS_HOST + GS_WPT_IMAGE_PATH + "4.gif",
text: "test notification, longer text, even longer text",
style: "blue"
}, {
title: "test 4",
icon: GS_HOST + GS_WPT_IMAGE_PATH + "5.gif",
text: "test notification"
}
],
dummyNoteZaehler = 0,
dummyNoteLength = dummyNote.length,
dummyNoteInterval,
foo = function() {
if ((0 <= dummyNoteZaehler) && (dummyNoteZaehler <= dummyNote.length)) {
$.gctour.notification.add(dummyNote[dummyNoteZaehler]);
}
dummyNoteZaehler++;
if (dummyNoteZaehler >= dummyNoteLength) {
clearInterval(dummyNoteInterval);
dummyNoteZaehler = 0;
}
};
$('#ctl00_ContentBanner_lnkNewDashboardBannerLink').on("mouseenter", function() {
clearInterval(dummyNoteInterval);
dummyNoteZaehler = 0;
dummyNoteInterval = setInterval(foo, 500);
});
}
}
$.gctour.notification.init();
//$.gctour.notification.test();
/* Leaflet maps for autoTour, change of coordinates and creating own waypoints */
function LeafletMap(options) {
if (typeof L !== "object") { // leaflet not available
addErrorDialog({
caption : "Map error",
_document : document,
_exception : 'Leaflet not available'
});
}
this._options = options;
this._id = options.id;
this._marker = '';
this._circle = '';
this._features = [];
this.init();
}
LeafletMap.prototype.init = function() {
// add leaflet css
if ($("head link#gct-leaflet-css").length === 0) { // has to be added only once
$("head").append('<link id="gct-leaflet-css" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css" rel="stylesheet" type="text/css">');
}
// add map
$('<div/>', {
id: this._id,
height: (this._options.height) ? this._options.height : '280px'
}).appendTo(this._options.anchor);
// Use local Leaflet
const L = window.L;
this._lm = L.map(this._id, {
maxZoom: 15,
boxZoom: false,
doubleClickZoom: false,
dragging: false,
keyboard: false,
prefix: false,
scrollWheelZoom: false,
tap: false,
touchZoom: false,
zoomControl: false,
zoomSnap: 0.1
});
this._lm.setView(this._options.center, 15);
const cs = L.control.scale().addTo(this._lm);
$(cs._container).css('left', 0);
// Base maps
const osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
const gm = L.tileLayer('https://{s}.google.com/vt?&x={x}&y={y}&z={z}', {
attribution: '&copy; <a href="https://maps.google.com/">Google Maps</a>',
maxZoom: 22,
subdomains: ['mt0', 'mt1', 'mt2', 'mt3']
});
// Load default map
switch (GM_getValue("defaultLeafletMap", "Google Maps")) {
case 'OpenStreetMap':
osm.addTo(this._lm);
break;
case 'Google Maps':
default:
gm.addTo(this._lm);
break;
}
// Add layer control
const baseMaps = {
"OpenStreetMap": osm,
"Google Maps": gm
};
const cl = L.control.layers(baseMaps, null, { position: 'topright' }).addTo(this._lm);
// GME decreases z-index of top-right div in leaflet control container; as a consequence layer control isn't visible
if ($(cl._container).parent().css('z-index') !== '999') {
$(cl._container).parent().css('z-index', 999);
}
// All layer controls that were not created by GCLH are removed (by GCLH), on both map pages
if ($('#GClh_II_running')[0]) {
$(cl._container).addClass('gclh_used');
}
// Decrease size of layer control slightly
$(cl._container).children().first().css({ 'width': '36px', 'height': '36px' });
// Make map selection persistent
this._lm.on('baselayerchange', function(e) {
GM_setValue('defaultLeafletMap', e.name);
});
}
LeafletMap.prototype.addMarker = function(marker) {
let size = 20, // px
ico,
GSIcon,
GCTIcon;
switch(marker.type) {
case 'gs': // GS marker
GSIcon = L.Icon.extend({
options: {
iconSize: [size, size+3],
iconAnchor: [size/2, size]
}
});
ico = new GSIcon({iconUrl: '/images/wpttypes/pins/'+marker.icon+'.png'});
this._marker = L.marker(marker.center, {icon: ico});
break;
case 'gct': // GCTour marker
GCTIcon = L.Icon.extend({
options: {
iconSize: [size, size],
iconAnchor: [size/2, size]
}
});
ico = new GCTIcon({iconUrl: GCTOUR_HOST+'i/'+marker.icon+'.png'});
this._marker = L.marker(marker.center, {icon: ico});
break;
default:
this._marker = L.marker(marker.center);
}
this._features.push(this._marker);
let group = new L.featureGroup(this._features);
group.addTo(this._lm);
this._lm.fitBounds(group.getBounds().pad(0.1));
}
LeafletMap.prototype.addCircle = function(circle) {
this._circle = L.circle(circle.center, circle.radius/*[m]*/);
this._features.push(this._circle);
let group = new L.featureGroup(this._features);
group.addTo(this._lm);
this._lm.fitBounds(group.getBounds().pad(0.1));
}
LeafletMap.prototype.removeLayer = function() {
if (this._features.length > 0) {
this._lm.removeLayer(this._features.pop());
}
}
/* own marker */
async function showNewMarkerDialog(marker) {
// create overlay
let overlayMarker = getOverlay({
caption : '<b>' + $.gctour.lang('printview.marker') + '</b>',
minimized : true
});
// hidden error field
let dangerDanger = document.createElement('div');
dangerDanger.id = "dangerdanger";
dangerDanger.style.visibility = "hidden";
dangerDanger.style.cssFloat = "right";
dangerDanger.innerHTML = "<img src='" + $.gctour.img.danger + "'>";
overlayMarker.appendChild(dangerDanger);
// content table
let anTable = document.createElement('table');
anTable.style.width = '100%';
anTable.style.clear = 'both';
anTable.align = 'center';
overlayMarker.appendChild(anTable);
// row name
let tr = document.createElement('tr');
anTable.appendChild(tr);
let td = document.createElement('td');
td.style.width = '20%';
td.textContent = 'Name';
tr.appendChild(td);
// input field for name of marker
td = document.createElement('td');
tr.appendChild(td);
let nameInput = document.createElement('input');
nameInput.type = 'text';
nameInput.id = 'markerName';
nameInput.className = "gctour-input-text";
nameInput.style.width = '450px';
td.appendChild(nameInput);
// row name
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
tr.appendChild(td);
td.textContent = $.gctour.lang('marker.coord');
// input field for marker coords
td = document.createElement('td');
tr.appendChild(td);
// lat/lon input field
let cordsInput = document.createElement('input');
cordsInput.type = 'text';
cordsInput.id = 'markerCoords';
cordsInput.className = "gctour-input-text";
cordsInput.style.width = '450px';
cordsInput.style.marginRight = '5px';
cordsInput['placeholder'] = 'N49° 26.082 E7° 46.587';
td.appendChild(cordsInput);
// example coords for lat/lon input field
let exampleCoords = document.createElement('div');
exampleCoords.innerHTML = '<div style="font-size:xx-small"><i>' + $.gctour.lang('general.exampleCoords') + '</i></div>';
td.appendChild(exampleCoords);
let defaultMarker = {};
defaultMarker.center = [0,0];
defaultMarker.type = 'gct';
defaultMarker.icon = 'RedFlag';
// check marker coords as you type
let checkMarkerCoord = function(input) {
return async function() {
let coords = await parseCoordinates(input.value);
if (coords === false) {
cordsInput.style.backgroundColor = "#FF8888";
} else {
cordsInput.style.backgroundColor = "#88DC3B";
// add marker
defaultMarker.center = [coords._lat, coords._lon];
lm.removeLayer(); // delete previous marker
lm.addMarker(defaultMarker);
}
};
};
cordsInput.addEventListener('keyup', checkMarkerCoord(cordsInput), false);
cordsInput.addEventListener('paste', checkMarkerCoord(cordsInput), false);
// add map
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
tr.appendChild(td);
td = document.createElement('td');
td.align = 'left';
tr.appendChild(td);
let map = document.createElement('div');
td.appendChild(map);
let options = {};
options.id = 'gct-marker-map';
options.center = [0,0];
options.height = '250px';
options.anchor = $(map);
let lm = new LeafletMap(options);
// row name
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
td.innerHTML = $.gctour.lang('marker.content') + '<br/><div style="font-size:xx-small">(' + $.gctour.lang('marker.contentHint') + ')</div>';
tr.appendChild(td);
// text content of marker
td = document.createElement('td');
tr.appendChild(td);
let contentTextarea = document.createElement('textarea');
contentTextarea.style.width = '450px';
contentTextarea.style['border'] = '1px solid #ccc';
contentTextarea.style['border-radius'] = '3px';
contentTextarea.id = 'markerContent';
contentTextarea.rows = '2';
td.appendChild(contentTextarea);
// row name
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
td.style.width = '20%';
td.textContent = $.gctour.lang('marker.type');
tr.appendChild(td);
// marker icons
td = document.createElement('td');
tr.appendChild(td);
let markerTypeTable = createElement('table', {
style : "width:auto;"
});
td.appendChild(markerTypeTable);
markerTypeTable.id = 'markerType';
let typeArray = [
[GCTOUR_HOST + 'i/RedFlag.png', 'Red Flag'],
[GCTOUR_HOST + 'i/BlueFlag.png', 'Blue Flag'],
[GCTOUR_HOST + 'i/GreenFlag.png', 'Green Flag'],
[GCTOUR_HOST + 'i/Geocache.png', 'Geocache'],
[GCTOUR_HOST + 'i/GeocacheFound.png', 'Geocache Found'],
[GCTOUR_HOST + 'i/Information.png', 'Information'],
[GCTOUR_HOST + 'i/Park.png', 'Park'],
[GCTOUR_HOST + 'i/ParkingArea.png', 'Parking'],
[GCTOUR_HOST + 'i/SkullAndBones.png', 'Skull And Crossbones']
];
let trElement = createElement('tr', {
style : "height:27px;"
});
markerTypeTable.appendChild(trElement);
for (let i = 0; i < typeArray.length; i++) {
let tdElement = createElement('td', {
style : "width:25px;"
});
tdElement.style.background = "url(" + typeArray[i][0] + ") center center no-repeat";
if (!marker) {
if (i === 0) {
tdElement.style.backgroundColor = '#B2D4F3';
}
} else {
if (typeArray[i][0] == marker.image) {
tdElement.style.backgroundColor = '#B2D4F3';
}
}
tdElement.style.cursor = 'pointer';
tdElement.style.padding = '0px';
tdElement.style.border = '1px solid silver';
tdElement.addEventListener('click', function(){
defaultMarker.icon = typeArray[i][0].split("/").pop().split(".")[0];
// emphasize selected icon
let $icons = $("table#markerType td");
$icons.css("background-color", "");
$($icons[i]).css("background-color", "#B2D4F3");
// update marker
lm.removeLayer();
lm.addMarker(defaultMarker);
}, false);
trElement.appendChild(tdElement);
}
// add save and cancel buttons
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
td.colSpan = '2';
td.align = 'right';
tr.appendChild(td);
// vars for saving marker to tour
let latitude = '';
let longitude = '';
let wptCode = '';
// button container
let buttonsDiv = createElement('div');
buttonsDiv.setAttribute('class', 'dialogFooter');
append(buttonsDiv, overlayMarker);
// cancel button
let cancel = createElement('input', {
type : "button",
value : $.gctour.lang('general.cancel'),
style : "background-image:url(" + $.gctour.img.closebutton + ")"
});
append(cancel, buttonsDiv);
cancel.addEventListener('click', closeOverlay, false);
// save button
let save = createElement('input', {
type : "button",
value : $.gctour.lang('general.save'),
style : "background-image:url(" + $.gctour.img.save + ")"
});
append(save, buttonsDiv);
save.addEventListener('click', function() {
let errors = 0;
let markerName = document.getElementById('markerName');
if (markerName.value != "") {
markerName.style.backgroundColor = "#FFFFFF";
} else {
markerName.style.backgroundColor = "#FF8888";
errors++;
}
let markerCoords = document.getElementById('markerCoords');
if (markerCoords.style.backgroundColor != "rgb(136, 220, 59)") {
markerCoords.style.backgroundColor = "#FF8888";
errors++;
}
let markerContent = document.getElementById('markerContent');
let markerType = GCTOUR_HOST + 'i/' + defaultMarker.icon + '.png';
let markerTypeSym = $.map(typeArray, function(value) {
if (value[0] === markerType) {
return value[1];
}
});
if (errors !== 0) {
document.getElementById('dangerdanger').style.visibility = "visible";
return;
}
latitude = defaultMarker.center[0];
longitude = defaultMarker.center[1];
let markerPositionDelta;
if (marker) {
let markerPosition = getPositionOfId(marker.wptcode);
markerPositionDelta = markerPosition - CURRENT_TOUR.geocaches.length + 1;
deleteElement((marker.id) ? marker.id : marker.wptcode)();
} else {
markerPositionDelta = 0;
}
let entry = addCustomMarker(markerName.value, latitude, longitude, markerContent.value, markerType, markerTypeSym, wptCode);
move(entry.wptcode, markerPositionDelta);
updateGUI();
closeOverlay();
}, false);
// if we edit a marker then set all previous values
if (marker) {
nameInput.value = marker.name;
latitude = marker.latitude;
longitude = marker.longitude;
wptCode = marker.wptcode;
let latLon = new LatLon(marker.latitude, marker.longitude);
cordsInput.value = latLon.toString("dm");
cordsInput.style.backgroundColor = "#88DC3B";
contentTextarea.innerHTML = marker.content;
defaultMarker.icon = marker.image.split('/').pop().split('.')[0];
} else {
let latlon;
// use listing coords when on a cache page and home coords otherwise
if (!(latlon = $('span#uxLatLon').text())) {
latlon = await getHomeCoords();
}
nameInput.value = 'WP';
cordsInput.value = latlon;
}
// set initial marker
checkMarkerCoord(cordsInput)();
// set the focus to coords input
cordsInput.focus();
}
/* change coordinates */
function displayChangedCoordinates(coordinates) {
let coordinates_org,
coordinates_ele = $('#uxLatLon');
try { // if coordinates were already changed by GCTour, extract original coordinates
coordinates_org = coordinates_ele.text().split("(")[1].split(")")[0];
} catch (e) { // no previous changes by GCTour
coordinates_org = coordinates_ele.text();
}
if (!coordinates) { // display original coords
coordinates_ele.html(coordinates_org);
} else { // display new coords
coordinates_ele.html(
"<small><div style='font-weight:bold;'>" + coordinates +
"&nbsp;&nbsp;-&nbsp;&nbsp;changed by GCTour</div> (" + coordinates_org + ")</small>");
}
}
async function openChangeCoordinates() {
// content table
let anTable = createElement('table', {
style : "clear:both;"
});
anTable.style.width = '100%';
anTable.align = 'center';
// create overlay
let overlayMarker = getOverlay({
caption : $.gctour.lang('cache.moveCoords'),
minimized : true
});
overlayMarker.appendChild(anTable);
// help section
let tr = document.createElement('tr');
anTable.appendChild(tr);
let td = document.createElement('td');
td.colSpan = 2;
td.innerHTML = $.gctour.lang('cache.moveCoordsHelp');
tr.appendChild(td);
// original coords name
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
td.style.width = '20%';
td.textContent = $.gctour.lang('cache.originalCoords');
tr.appendChild(td);
// get original coordinates
let coordinates = $('#uxLatLon').text();
try {
// if coordinates were already changed by GCTour, extract original coordinates
coordinates = coordinates.split("(")[1].split(")")[0];
} catch (e) {
// no previous changes by GCTour
}
// cache details
let minimal_geocache = getMinimalGeocacheDetails(document.getElementsByTagName('html')[0]);
let gc_type = minimal_geocache.type;
let coords = await parseCoordinates(coordinates);
let cacheId = minimal_geocache.gccode;
// original coords value
td = document.createElement('td');
tr.appendChild(td);
let nameInput = document.createElement('input');
nameInput.type = 'text';
nameInput.id = 'markerName';
nameInput.className = "gctour-input-text";
nameInput.value = coords;
nameInput.style.width = '350px';
nameInput.style.marginRight = '5px';
nameInput.disabled = 'disabled';
td.appendChild(nameInput);
// new coords name
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
td.textContent = $.gctour.lang('cache.newCoords');
tr.appendChild(td);
// new coords value
td = document.createElement('td');
tr.appendChild(td);
// hidden lat value (decimal)
let cordsInputLat = document.createElement('input');
cordsInputLat.type = "hidden";
cordsInputLat.id = 'cordsInputLat';
td.appendChild(cordsInputLat);
// hidden lon value (decimal)
let cordsInputLon = document.createElement('input');
cordsInputLon.type = "hidden";
cordsInputLon.id = 'cordsInputLon';
td.appendChild(cordsInputLon);
// visible lat/lon value
let cordsInput = document.createElement('input');
cordsInput.type = 'text';
cordsInput.id = 'markerCoords';
cordsInput.className = "gctour-input-text";
cordsInput.style.width = '350px';
cordsInput.style.marginRight = '5px';
td.appendChild(cordsInput);
// add example coords text to new coords input
let exampleCoords = document.createElement('div');
exampleCoords.innerHTML = '<div style="font-size:x-small"><i>' + $.gctour.lang('general.exampleCoords') + '</i></div>';
td.appendChild(exampleCoords);
// process new coords as you type
let checkMarkerCoord = function(input) {
return async function() {
let coords = await parseCoordinates(input.value);
if (coords === false) {
cordsInput.style.backgroundColor = "#FF8888";
} else {
cordsInput.style.backgroundColor = "#88DC3B";
cordsInputLat.value = coords._lat;
cordsInputLon.value = coords._lon;
// update marker on map
let marker = {};
marker.center = [cordsInputLat.value, cordsInputLon.value];
marker.type = 'gs';
marker.icon = gc_type.split(".")[0];
lm.removeLayer(); // remove previous marker first
lm.addMarker(marker);
}
};
};
// check new coords on key input or paste
cordsInput.addEventListener('keyup', checkMarkerCoord(cordsInput), false);
cordsInput.addEventListener('paste', checkMarkerCoord(cordsInput), false);
// map section
let mapTd = document.createElement('td');
mapTd.align = 'left';
// add map to table
tr = document.createElement('tr');
anTable.appendChild(tr);
td = document.createElement('td');
tr.appendChild(td);
tr.appendChild(mapTd);
let options = {};
options.id = 'gct-changeCoords-map';
options.center = [coords._lat, coords._lon];
options.height = '250px';
options.anchor = $(mapTd);
let lm = new LeafletMap(options);
// save/delete/cancel buttons
// container for buttons
let buttonsDiv = createElement('div');
append(buttonsDiv, overlayMarker);
buttonsDiv.setAttribute('class', 'dialogFooter');
// cancel button
let cancel = createElement('input', {
type : "button",
value : $.gctour.lang('general.cancel'),
style : "background-image:url(" + $.gctour.img.closebutton + ")"
});
append(cancel, buttonsDiv);
cancel.addEventListener('click', closeOverlay, false);
// delete button
let delete_btn = createElement('input', {
type : "button",
value : $.gctour.lang('cache.deleteCoords'),
style : "background-image:url(" + $.gctour.img.closebutton + ")"
});
append(delete_btn, buttonsDiv);
delete_btn.addEventListener('click', function() {
GM_deleteValue('coords_' + cacheId);
displayChangedCoordinates();
updateGUI();
closeOverlay();
}, false);
// save button
let save = createElement('input', {
type : "button",
value : $.gctour.lang('general.save'),
style : "background-image:url(" + $.gctour.img.save + ")"
});
append(save, buttonsDiv);
save.addEventListener('click', function() {
GM_setValue('coords_' + cacheId, cordsInputLat.value + '#' + cordsInputLon.value);
displayChangedCoordinates(new LatLon(cordsInputLat.value, cordsInputLon.value).toString());
updateGUI();
closeOverlay();
}, false);
// initialize
let latlng;
if (GM_getValue('coords_' + cacheId, "null") != "null") { // coords have been changed before
var coords_cacheId = GM_getValue('coords_' + cacheId);
latlng = new LatLon(coords_cacheId.split('#')[0], coords_cacheId.split('#')[1]);
} else { // original coords
latlng = await parseCoordinates(coordinates);
}
cordsInputLat.value = latlng._lat;
cordsInputLon.value = latlng._lon;
cordsInput.value = latlng.toString();
cordsInput.style.backgroundColor = "#88DC3B";
let marker = {};
marker.center = [cordsInputLat.value, cordsInputLon.value];
marker.type = 'gs';
marker.icon = gc_type.split(".")[0];
lm.addMarker(marker);
}
/* tour functions */
function getTourById(id) {
for (var i = 0; i < TOURS.length; i++) {
if (TOURS[i].id == id) {
return TOURS[i];
}
}
}
function getNewTourId() {
var tourId = 0;
for (var i = 0; i < TOURS.length; i++) {
if (TOURS[i].id >= tourId) {
tourId = TOURS[i].id + 1;
}
}
return tourId;
}
function isGcIdInTable(gcId) {
for (var i = 0; i < CURRENT_TOUR.geocaches.length; i++) {
if (CURRENT_TOUR.geocaches[i].id == gcId) {
return true;
}
}
return false;
}
function getPositionOfId(theId) {
var id = -1;
$.each(CURRENT_TOUR.geocaches, function(i, obj) {
if (obj.id == theId || obj.wptcode == theId) {
id = i;
return false; // break each
}
});
return id;
}
function saveTour(tour, notLoad) {
var i;
for (i = 0; i < TOURS.length; ++i) {
if (TOURS[i].id == tour.id) {
TOURS[i] = tour;
}
}
GM_setValue('tours', JSON.stringify(TOURS));
if (notLoad === undefined) {
GM_setValue('currentTour', tour.id);
log("updating " + tour.name);
}
}
function saveCurrentTour() {
saveTour(CURRENT_TOUR);
}
// move a cache to a given position in the list
function move(id, positionDelta) {
var i;
// locate the selected cache in the list
var position = getPositionOfId(id);
// return if we are at the end or at top of the list!
if ((position === 0 && positionDelta < 0) || (position == CURRENT_TOUR.geocaches.length - 1 && positionDelta > 0)) {
return;
}
// save clicked cache
var geoCache = CURRENT_TOUR.geocaches[position];
// remove it from the current geocaches
CURRENT_TOUR.geocaches.splice(position, 1);
var tempCaches = [];
// first push all caches in front of the selected in the new array
for (i = 0; i < position + positionDelta; i++) {
tempCaches.push(CURRENT_TOUR.geocaches[i]);
}
// then the selected
tempCaches.push(geoCache);
// and now the rest
for (i = position + positionDelta; i < CURRENT_TOUR.geocaches.length; i++) {
tempCaches.push(CURRENT_TOUR.geocaches[i]);
}
// ... and make it persistent
CURRENT_TOUR.geocaches = tempCaches;
saveCurrentTour();
updateTourNumbering();
}
function moveUp(id) {
return function() {
move(id, -1);
};
}
function moveDown(id) {
return function() {
move(id, 1);
};
}
function moveTop(id) {
return function() {
var position = getPositionOfId(id);
move(id, -position);
};
}
function moveBottom(id) {
return function() {
var position = getPositionOfId(id);
move(id, CURRENT_TOUR.geocaches.length - position - 1);
};
}
function updateCacheCount(count) {
$("#cachecount")
.html('(' + count + ')')
.stop(true)
.animate({
backgroundColor : '#ffe000'
}, 800)
.animate({
backgroundColor : '#ffffff'
}, 700);
if (!STICKY) { // nur wenn sichtbar
$("#gctourButtonWrapper")
.stop(true)
.toggleClass("gctour-grand-highlight", 300)
.toggleClass("gctour-grand-highlight", 1200);
}
}
function updateTourNumbering() {
$('ul#cacheList div.counter').each(function(i) {
$(this).text(i+1);
});
}
function saveCurrentTourRefreshGUI() {
saveCurrentTour();
updateTourNumbering();
updateCacheCount(CURRENT_TOUR.geocaches.length);
}
function addToCacheList(theEntry, effects) {
var costumMarker = (typeof(theEntry.latitude) !== "undefined");
// if this is a custom marker user other id
var theId = (!costumMarker) ? theEntry.id : theEntry.wptcode;
var entryLi = createElement('li', {
id : theId,
style : "position:relative;opacity:0;width:90%;list-style-image='url('" + theEntry.image + "');background-color:pink;"
});
//set the type
entryLi.style.listStyleImage = "url('" + theEntry.image + "')";
// make the gcid link
var nameCite = createElement('span', {
style : "vertical-align:top"
});
if (!costumMarker) {
var coordinates = GM_getValue('coords_' + theId, "null"); // changed by GCTour
if (coordinates != "null") {
var moveCoords = createElement('img', {
src : $.gctour.img.coord_update,
height : "12",
style : "float:right;margin-right:5px;margin-top:2px",
alt : $.gctour.lang('cache.movedCoords'),
title : $.gctour.lang('cache.movedCoords')
});
nameCite.appendChild(moveCoords);
}
var linkElement = document.createElement('a');
linkElement.style.fontFamily = 'arial,sans-serif';
linkElement.href = 'http://coord.info/' + theId;
linkElement.target = '_blank';
linkElement.textContent = theId;
nameCite.appendChild(linkElement);
} else {
nameCite.innerHTML += theEntry.name;
nameCite.style.textDecoration = "underline";
}
// the log/edit, move and delete button
var functionButtonsDiv = document.createElement('div');
functionButtonsDiv.style.cssFloat = 'right';
functionButtonsDiv.setAttribute("class", "controls");
if (!costumMarker) {
var logVisitImage = document.createElement('img');
logVisitImage.alt = $.gctour.lang('container.cacheList.logYourVisit');
logVisitImage.title = $.gctour.lang('container.cacheList.logYourVisit');
logVisitImage.style.cursor = 'pointer';
logVisitImage.src = $.gctour.img.add_comment;
logVisitImage.addEventListener('click', function() {
GM_openInTab(GS_HOST + 'seek/log.aspx?wp=' + theId, false);
}, true);
addOpacityEffects(logVisitImage);
functionButtonsDiv.appendChild(logVisitImage);
} else {
var editMarkerButton = document.createElement('img');
editMarkerButton.alt = $.gctour.lang('general.edit');
editMarkerButton.title = $.gctour.lang('general.edit');
editMarkerButton.style.cursor = 'pointer';
editMarkerButton.src = $.gctour.img.edit;
editMarkerButton.addEventListener('click', function() {
showNewMarkerDialog(theEntry);
}, false);
addOpacityEffects(editMarkerButton);
functionButtonsDiv.appendChild(editMarkerButton);
}
// button to move an element to a different tour
var moveImage = document.createElement('img');
moveImage.alt = $.gctour.lang('container.cacheList.moveToList');
moveImage.title = $.gctour.lang('container.cacheList.moveToList');
moveImage.style.cursor = 'pointer';
moveImage.style.marginLeft = '1px';
moveImage.style.marginRight = '1px';
moveImage.src = $.gctour.img.move;
moveImage.addEventListener('click', function() {
openTourDialog(theId);
});
addOpacityEffects(moveImage);
functionButtonsDiv.appendChild(moveImage);
// button to remove an element from a tour
var deleteImage = document.createElement('img');
deleteImage.alt = $.gctour.lang('container.cacheList.removeFromList');
deleteImage.title = $.gctour.lang('container.cacheList.removeFromList');
deleteImage.style.cursor = 'pointer';
deleteImage.src = $.gctour.img.del;
deleteImage.addEventListener('click', deleteElement(theId), true);
addOpacityEffects(deleteImage);
functionButtonsDiv.appendChild(deleteImage);
entryLi.appendChild(functionButtonsDiv);
entryLi.appendChild(nameCite);
var nameDiv = document.createElement('div');
nameDiv.style.clear = 'left';
nameDiv.style.position = 'relative';
nameDiv.style.zIndex = 2;
nameDiv.style.width = '90%';
// truncate single words that are too long
nameDiv.style.overflow = 'hidden';
nameDiv.style.textOverflow = 'ellipsis';
if (!costumMarker) {
nameDiv.innerHTML += theEntry.name;
} else {
nameDiv.innerHTML += new LatLon(theEntry.latitude, theEntry.longitude).toString() + " " + theEntry.content;
}
entryLi.appendChild(nameDiv);
var counterDiv = document.createElement('div');
counterDiv.className = 'counter unselectable';
counterDiv.innerHTML = (getPositionOfId(theEntry.id || theEntry.wptcode) + 1);
entryLi.appendChild(counterDiv);
$('#cacheList').append(entryLi);
if (unsafeWindow.draglist) {
unsafeWindow.draglist.sync(); // needed to function properly
}
if (effects) { // when adding more than one cache this gets quite slow ...
$("#" + theId).fadeTo(1000, 1);
} else {
$("#" + theId).css({
opacity : 1
});
}
}
function addToCurrentTour(entry) {
var currentTourId = GM_getValue('currentTour', -1);
CURRENT_TOUR = getTourById(currentTourId);
CURRENT_TOUR.geocaches.push(entry);
log("saving " + (entry.id ? entry.id : entry.wptcode) + " to " + CURRENT_TOUR.name);
}
function addCustomMarker(name, lat, lon, content, typeImage, typeSymbol, wptcode) {
// customMarker:
// name -> cachename parking area
// lat -> latitude 51.12342
// lon -> longitude -12.33456
// content -> content "Test\nLINEBREAK"
// image -> typeimage https://www.gctour.de/i/ParkingArea.png
// symbol -> GPX symbol name "Red Flag"
// wptcode -> waypoint id 1664d58479d
if (CURRENT_TOUR.geocaches.length === 0) {
$('ul#cacheList').html('');
}
var entry = {};
entry.wptcode = (wptcode) ? wptcode : (new Date().getTime() - Math.round(lat + lon * 1000)).toString(16);
entry.name = name;
entry.latitude = lat;
entry.longitude = lon;
entry.image = typeImage;
entry.content = content;
entry.symbol = typeSymbol;
log("New custom marker: " + entry.name + " lat:" + entry.latitude + " lon:" + entry.longitude + " Type:" + entry.symbol + " content:" + entry.content);
// add the newbie
addToCacheList(entry, true);
addToCurrentTour(entry);
return entry;
}
function addGeocache(theId, theGuId, theName, theTypeImage) {
return function() {
if (CURRENT_TOUR.geocaches.length === 0) {
$('ul#cacheList').html('');
}
if (!isGcIdInTable(theId)) {
// entry:
// id -> the gc.com id GC00815
// guid -> the guid 6e974919-2b47-46e2-8661-3fc62a5a9650
// name -> the cachename Echo the tomcat
// image -> the typeimage 2.gif
var entry = {};
entry.id = theId;
entry.name = escapeHTML(theName);
entry.guid = theGuId;
entry.image = GS_HOST + GS_WPT_IMAGE_PATH + theTypeImage;
// add the newbie
addToCacheList(entry, false);
addToCurrentTour(entry);
showGeocacheNotification({
id : theId,
name : theName,
image : GS_HOST + GS_WPT_IMAGE_PATH + theTypeImage,
tourname : CURRENT_TOUR.name
}, {
type : "success"
});
} else {
showGeocacheNotification({
id : theId,
name : theName,
image : GS_HOST + GS_WPT_IMAGE_PATH + theTypeImage,
tourname : CURRENT_TOUR.name
}, {
type : "contains"
});
}
};
}
function deleteElement(theId) {
return function() {
let delay = 500;
// effect
$("#" + theId)
.fadeOut(delay, function() {
$(this).remove();
});
// locate the element to delete
for (var i = 0; i < CURRENT_TOUR.geocaches.length; i++) {
if (CURRENT_TOUR.geocaches[i].id == theId || CURRENT_TOUR.geocaches[i].wptcode == theId) {
CURRENT_TOUR.geocaches.splice(i, 1);
log("removing '" + theId + "' from '" + CURRENT_TOUR.name + "'");
break;
}
}
saveCurrentTour();
// update the cache count
updateCacheCount(CURRENT_TOUR.geocaches.length);
// update numbering
setTimeout(function() {
updateTourNumbering();
}, delay);
// if tour is empty display hint
if (CURRENT_TOUR.geocaches.length === 0) {
$('#cacheList').html($.gctour.lang('tour.empty'));
}
};
}
function removeElements(descriptionElement, id, tagName) {
return function() {
var elements = descriptionElement.getElementsByTagName(tagName);
for (var x = 0; x < elements.length; x++) {
if (elements[x].id == id) {
elements[x].style.display = "none";
}
}
};
}
function updateTour() {
initCore();
updateGUI();
}
function loadTour(id) {
return function() {
GM_setValue('currentTour', id);
if (document.URL.search("webcode") >= 0) {
window.location = GS_HOST;
} else {
updateTour();
}
};
}
function newTourFunction(preset) {
return function() {
var newTour = {};
newTour.id = getNewTourId();
var tourName = (preset) ? preset : "Tour " + newTour.id;
newTour.name = prompt($.gctour.lang('tour.newDialog'), tourName);
newTour.geocaches = [];
if (!newTour.name) {
return false;
}
TOURS.push(newTour);
log("Creating new tour: " + newTour.id + " ; " + newTour.name);
saveTour(newTour);
updateTour();
return true;
};
}
function deleteTour(id, force) {
return function() {
if (force || confirm($.gctour.lang('tour.deleteDialog'))) {
for (var i = 0; i < TOURS.length; i++) {
if (TOURS[i].id == id) {
log("removing '" + TOURS[i].name + "'");
var cachelist = $('#dialogDetails');
if (cachelist.length > 0 && cachelist.attr("tourid") == TOURS[i].id) {
showCacheList(CURRENT_TOUR)();
$('#loadButton').attr("disabled", "disabled");
}
$("#tour" + id).remove();
if (TOURS[i].bml) {
deleteBookmarklist(TOURS[i].bml);
}
TOURS.splice(i, 1);
saveCurrentTour();
break;
}
}
}
};
}
function deleteCurrentTour(force=false) {
if (force || confirm($.gctour.lang('tour.deleteDialog'))) {
var tableId;
for (tableId = 0; tableId < TOURS.length; tableId++) {
if (TOURS[tableId].id == CURRENT_TOUR.id) {
break;
}
}
var nextTourId = TOURS[(tableId + 1) % TOURS.length].id;
var currentTourId = CURRENT_TOUR.id;
loadTour(nextTourId)();
deleteTour(currentTourId, true)();
}
}
function sortCurrentTourAlphabetically() {
CURRENT_TOUR.geocaches = CURRENT_TOUR.geocaches.sort((a, b) => a.name.localeCompare(b.name, undefined, { 'numeric': true }));
saveCurrentTour();
loadTour(CURRENT_TOUR.id)();
debug("tour sorted alphabetically");
}
/* printpage functions */
async function printPageFunction(tour) {
if (isEmptyTour(tour) || !isLoggedIn()) {
return;
}
var i,
waypoint_i,
tr,
td,
nCaches = tour.geocaches.length,
minimal = GM_getValue('printMinimal', false);
var cacheDetailTemplate =
'<div class="cacheDetail ###HIDDENSTYLE###" id="###GUID###">' +
' <div class="geocache_count ###HIDDENSTYLE###"><span>###CACHECOUNT###</span></div>' +
' <div class="geocache_id">###GCID###</div>' +
' <div>' +
' <img src="' + $.gctour.img.wpt_type + '">' +
' <span style="font-weight: bold;">###CACHENAME###</span>' +
' <span style="margin-right: 3px;"> (###OWNER### - ###HIDDEN###)</span>' +
' </div>' +
' <div class="details">' +
' <span style="white-space: nowrap;"><img src="' + $.gctour.img.coord_update + '" height="12px" class="###COORDINATESISEDIT###"/> ###COORDINATES###</span>' +
' <span style="white-space: nowrap;"><img src="' + $.gctour.img.bearing + '"/>###DISTANCE###&nbsp;</span>' +
' <span style="white-space: nowrap;">D:<img src="' + $.gctour.img.stars_d + '"/></span>' +
' <span style="white-space: nowrap;">T:<img src="' + $.gctour.img.stars_t + '"/></span>' +
' <span style="white-space: nowrap;">S:<img src="' + $.gctour.img.cache_size + '"/></span>' +
' </div>' +
' <div>' +
' <span>###ATTRIBUTES###</span>' +
' <span><img alt="Inventory" style="padding-left: 10px;padding-right: 2px;" src="' + $.gctour.img.tb_coin + '"/>Inventory:</span>' +
' <span>###INVENTORY###</span>' +
' <span><img src="' + $.gctour.img.fav + '" style="padding-left: 10px;"/>Favorites: ###FAV### ###FAVSCORE###</span>' +
' </div>' +
' <div class="content">' +
' <div class="short ###HIDDENSTYLE###">###SHORT_DESCRIPTION###</div>' +
' <div class="long ###HIDDENSTYLE###">###LONG_DESCRIPTION###</div>' +
' <div>###GCCOMMENT###</div>' +
' <div class="removable">###CACHENOTE###</div>' +
' <div contenteditable="true"><b><u>Hint:</u></b>###HINT###</div>' +
' <div class="waypoints ###HIDDENSTYLE###">###ADDITIONAL_WAYPOINTS###</div>' +
' <div class="images">###IMAGES###</div>' +
' <div id = "###MAPID###" class="map ###HIDDENSTYLE###">###MAP###</div>' +
' <div class="removable ###HIDDENSTYLE###">###LOGCOUNTER###</div>' +
' <div class="logs ###HIDDENSTYLE###">###LOGS###</div>' +
' <div style="clear:both">&nbsp;</span>' +
' </div>' +
'</div>';
var ownMarkerTemplate =
'<div class="cacheDetail ###HIDDENSTYLE###">' +
' <div class="geocache_count ###HIDDENSTYLE###" style="padding:5px !important"><span>###CACHECOUNT###</span></div>' +
' <div class="wpt_id">###GCID###</div>' +
' <div>' +
' <img src="###TYPE###">' +
' <span style="font-weight: bold;">###NAME###</span><br/>' +
' <span>###COORDINATES###</span>' +
' </div>' +
' <div>' +
' <div class="long">###CONTENT###</div>' +
' </div>' +
'</div>';
// show/hide cache and own marker details (but only if title page is present and minimal printview is not set; otherwise print preview is completely empty)
if (GM_getValue('showCacheDetails', true)===false && GM_getValue('printFrontpage', true) && !minimal) {
cacheDetailTemplate = cacheDetailTemplate.replace("###HIDDENSTYLE###", "hidden");
ownMarkerTemplate = ownMarkerTemplate.replace("###HIDDENSTYLE###", "hidden");
} else {
cacheDetailTemplate = cacheDetailTemplate.replace("###HIDDENSTYLE###", "");
ownMarkerTemplate = ownMarkerTemplate.replace("###HIDDENSTYLE###", "");
}
// clear content
$('html').html(""); // result: <html><head></head><body></body></html>
// title of print preview page
$('html').append('<title>' + $.gctour.lang('settings.printview.header') + '</title>');
var bodyTag = document.getElementsByTagName('body')[0];
bodyTag.style.background = 'none';
bodyTag.style.backgroundColor = "white";
bodyTag.innerHTML = '';
// add styles to body tag
$('head').remove(); // presence of head tag causes problems when printing in Firefox --> add styles to body tag
$('<link/>').attr("href", "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css").attr("rel", "stylesheet").appendTo($(bodyTag));
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '*{ font-size:' + GM_getValue("printFontSize", "x-small") + ' } .cacheDetail{ border: 1px solid lightgray; width: 100%; text-align: left;padding:5px; -moz-box-sizing: border-box; } .wpt_id{ position:relative; padding:5px !important; float:right; font-weight:bold; } .geocache_id{ position:relative; padding:20px !important; padding-right: 5px !important; float:right; font-weight:bold; } .content{ clear:both; border-top:2px dashed lightgray; margin-top:10px; padding-top:10px; } img{ vertical-align:middle; } #details span{ margin-left: 10px } .images{clear:both;height:auto}' +
'.map{clear:both} .logs{clear:both} .hidden{display:none} .highlight{background-color:pink}' +
'.geocache_count{ position:relative; padding:20px !important; padding-left: 5px !important; padding-right: 5px !important; float:right; font-weight:bold; } .geocache_count span{padding: 5px; font-weight: bold; -moz-border-radius: 5px; border-radius: 5px; border:2px dotted black;}' +
'sup {vertical-align:baseline;font-size:77%;position:relative;top:-5px;}' +
'.dialogMask {background-image:url(' + $.gctour.img.dialogMask + ');height:100%;left:0;opacity:0.7;position:fixed;top:0;width:100%;z-index:1100;}' +
'.dialogBody{-moz-border-radius:5px; border-radius:5px; background:none repeat scroll 0 0 #fff;border:1px solid #333333;color:#333333;cursor:default;font-family:Arial;font-size:12px;left:50%;margin-left:-250px;margin-top:20px;padding:0 0 1em;position:fixed;text-align:left;top:0;width:500px;z-index:1101;max-height:85%;min-height:370px;overflow:auto;}' +
'.dialogBody p {font-size:12px;font-weight:normal;margin:1em 0em;}' +
'.dialogBody h1{background-color:#B2D4F3;font-size:110%;font-family:Helvetica Neue,Arial,Helvetica,sans-serif;margin-bottom:0.2em;padding:0.5em;-moz-border-radius:5px 5px 0px 0px;border-radius:5px 5px 0px 0px;color:#333333;background-image:url("' + $.gctour.img.tabBg + '");margin:0px;}' +
'.dialogHistory {border:1px inset #999999;margin:0 1em 1em;height:200px;overflow-y:auto;width:448px;padding-left:1em;}' +
'.dialogHistory ul{margin-left:2em;}' +
'.dialogHistory li{list-style-type:circle;}' +
'.dialogFooter input{-moz-border-radius:3px;border-radius:3px;background:none no-repeat scroll 4px center #EEEEEE;border:1px outset #666666;cursor:pointer;float:right;margin-left:0.5em;padding:3px 5px 5px 20px;min-width:100px;}' +
'.dialogFooter input:hover { background-color:#f9f9f9; }' +
'.dialogContent {padding:0px 10px 0px 10px;}' +
'.dialogMin {min-height:0px !important}' +
'.noprint {padding:2px;border: 1px solid #c0cee3;z-index: 10000;background-color: #eff4f9; text-align: left;margin-top:10px} .noprint>div {margin-top:2px} ' +
'.noprint>input {border: 1px outset #666666;cursor: pointer;margin:5px;padding: 3px 5px 5px 25px;background: none no-repeat scroll 4px center #eeeeee;float:left;clear:both;} ' +
'.noprint>input:hover {background-color:#f9f9f9}';
// limit the font size of the cache table to "small"; for bigger sizes the table most likely gets too wide
var fs = GM_getValue("printFontSize", "x-small");
if ((fs != "xx-small") && (fs != "x-small") && (fs != "small")) {
fs = "small";
}
style.innerHTML += 'div#printTitle>table * {font-size:' + fs + ';}' +
'.noprint * {font-size:' + fs + ';}';
bodyTag.appendChild(style);
style = document.createElement('style');
style.media = 'print';
style.type = 'text/css';
//hide the map control in print
style.innerHTML = '.noprint { display: none; } body {margin: 0;padding: 0;color: black;background: transparent;width:99%}';
bodyTag.appendChild(style);
var body = document.createElement('div');
var printviewWidthDefault = 648;
var printviewWidth = GM_getValue("printviewWidth", printviewWidthDefault);
$(body).width(printviewWidth+"px");
$(body).css("margin", "30px auto");
bodyTag.appendChild(body);
var $printInfo = $('<div/>', {
'class': 'noprint',
'html': $.gctour.lang('printview.dontPrintHint'),
'css': {
'font-size': fs
}
});
$(body).append($printInfo);
//////////////////////////////////////////////
// begin slider for changing width of print preview
var $div_width = $('<div/>', {
'class': 'noprint',
'html': $.gctour.lang('printview.printWidth'),
'css': {
'font-size': fs
}
}),
$slider = $('<div/>', {
'id': 'slider',
'css': {
'display': 'inline-block',
'margin-left': '10px',
'margin-right': '40px',
'top': '2px'
}
}),
$slider_handle = $('<div/>', {
'id': 'slider_handle',
'class': 'ui-slider-handle',
'css': {
'width': '3.5em',
'text-align': 'center',
'font-size': 'smaller',
'top': '-2.25px'
}
});
$(body).append(
$div_width.append(
$slider.append($slider_handle)));
var offset = 150;
$(function() {
var handle = $("#slider_handle");
$("#slider").slider({
value: printviewWidth,
min: 600,
max: $(document).width()-24,
create: function() {
handle.text($(this).slider("value") + "px");
$(this).width($("#slider").parent().width()-offset);
},
slide: function(event, ui) {
handle.text(ui.value + "px");
$("#slider").parent().parent().width(ui.value);
$(this).width($("#slider").parent().width()-offset);
},
stop: function() { // store current width as new default value
GM_setValue("printviewWidth", $(this).slider("value"));
}
});
});
// reset button
$("#slider").parent().append('<div id="reset" style="display: inline-block;"></div>');
$("#reset").button({
label: "Reset"
}).on('click', function() {
$("#slider").slider("value", printviewWidthDefault);
$("#slider_handle").text(printviewWidthDefault + "px");
$("#slider").parent().parent().width(printviewWidthDefault);
$("#slider").width($("#slider").parent().width()-offset);
GM_setValue("printviewWidth", printviewWidthDefault);
});
$("div#reset > span.ui-button-text").css("font-size","smaller");
// end slider for changing width of print preview
//////////////////////////////////////////////
addProgressbar({
_document : document,
closeCallback : function(_document) {
return function() {
if (confirm($.gctour.lang('general.cancel') + "?") == true) {
window.location.reload();
}
};
}
});
// for progress bar update
PROGRESS_BAR.progress = 0;
PROGRESS_BAR.total = nCaches;
$("<fieldset/>", {
'class' : 'noprint'
})
.css('right', '50px')
.css('position', 'fixed')
.append(
$("<legend/>").html($.gctour.lang('settings.printview.header'))
.css('background', "url(\"" + $.gctour.img.gctourLogoSmall + "\") no-repeat scroll 0 0 transparent")
.css('padding-left', '20px'),
$("<input/>").attr("type", "input").attr("value", $.gctour.lang('printview.print')).css("background-image", "url(" + $.gctour.img.printer + ")").on('click', function() {
self.print()
}),
$("<input/>").attr("type", "input").attr("value", $.gctour.lang('general.close')).css("background-image", "url(" + $.gctour.img.closebutton + ")").on('click', function () {
window.location.reload();
})).appendTo($(body));
// front page
if (GM_getValue('printFrontpage', true) && !minimal) {
var title = $('<div>', {
id : 'printTitle',
css : {
width : "100%",
textAlign : 'center',
"page-break-after" : ((GM_getValue('printPageBreakAfterMap', true)) ? 'always' : 'never')
},
html : "<h1>" + tour.name + "</h1>"
});
$(body).append(title);
var coverTable = document.createElement('table');
coverTable.style.width = "100%";
coverTable.style.border = '1px solid lightgray';
var str =
'<thead><tr> ' +
' <th colspan="2" style="border-bottom:1px solid lightgray;"><b>Geocache</b></th> ' +
' <th style="border-bottom:1px solid lightgray;">&nbsp;</th> ' +
' <th style="border-bottom:1px solid lightgray;">&nbsp;</th> ';
if (GM_getValue('printCacheTableD', true)) {
str += ' <th style="border-bottom:1px solid lightgray;"><b>D</b></th> ';
}
if (GM_getValue('printCacheTableT', true)) {
str += ' <th style="border-bottom:1px solid lightgray;"><b>T</b></th> ';
}
if (GM_getValue('printCacheTableS', true)) {
str += ' <th style="border-bottom:1px solid lightgray;"><b>S</b></th> ';
}
if (GM_getValue('printCacheTableL4L', true)) {
str += ' <th style="border-bottom:1px solid lightgray;"><b>L4L</b>&nbsp;</th> ';
}
if (GM_getValue('printCacheTableFav', true)) {
str += ' <th colspan="2" style="border-bottom:1px solid lightgray;"><b>Fav</b>&nbsp;</th> ';
}
str += ' <th style="border-bottom:1px solid lightgray;"><b>' + $.gctour.lang('marker.coord') + '</b></th> ';
if (GM_getValue('printCacheTableFound', true)) {
str += ' <th style="border-bottom:1px solid lightgray;"><b>' + $.gctour.lang('printview.found') + '</b></th> ';
}
if (GM_getValue('printCacheTableNote', true)) {
str += ' <th style="border-bottom:1px solid lightgray;">&nbsp;&nbsp;<b>' + $.gctour.lang('printview.note') + '</b></th> ';
}
str += '</tr><thead>';
coverTable.innerHTML = str;
var tbody = createElement('tbody');
append(tbody, coverTable);
// table of caches
var isCostumMarker = false,
id,
coords;
for (i = 0; i < nCaches; ++i) {
var costumMarker = (typeof(tour.geocaches[i].latitude) != "undefined");
if (costumMarker) {
id = "WP";
coords = "'coords_WP" + (i + 1) + "'>" + new LatLon(tour.geocaches[i].latitude, tour.geocaches[i].longitude).toString();
} else {
id = tour.geocaches[i].id;
coords = "'coords_" + tour.geocaches[i].id + "'>";
}
if (GM_getValue('addCustomMarkerToCacheTable', true) === true) {
costumMarker = false; // add custom marker to cache table
}
if (!costumMarker) {
tr = createElement('tr');
tbody.appendChild(tr);
// numbering
td = createElement('td');
tr.appendChild(td);
td.innerHTML = "<b style='margin:0 6px'>" + (i + 1) + "</b>";
// image
td = createElement('td', {
style : "border-bottom:1px solid lightgray;"
});
tr.appendChild(td);
td.innerHTML = "<img src='" + tour.geocaches[i].image + "'>";
// name
td = createElement('td', {
style : "text-align:left;border-bottom:1px solid lightgray;white-space:nowrap;"
});
tr.appendChild(td);
td.innerHTML = "<a style='color:#000000;text-decoration: none' target='_blank' href='http://coord.info/" + tour.geocaches[i].id + "'>" + tour.geocaches[i].name + "</a>";
// id
td = createElement('td', {
style : "text-align:left;border-bottom:1px solid lightgray;border-right:1px dashed lightgray;"
});
tr.appendChild(td);
td.innerHTML = "<span style='margin:0 2px'>" + id + "</span>";
// difficulty
if (GM_getValue('printCacheTableD', true)) {
td = createElement('td', {
style : "border-bottom:1px solid lightgray;border-right:1px dashed lightgray;"
});
tr.appendChild(td);
td.innerHTML = "<span style='margin:0 2px' id='d_" + tour.geocaches[i].id + "'></span>";
}
// terrain
if (GM_getValue('printCacheTableT', true)) {
td = createElement('td', {
style : "border-bottom:1px solid lightgray;border-right:1px dashed lightgray;"
});
tr.appendChild(td);
td.innerHTML = "<span style='margin:0 2px' id='t_" + tour.geocaches[i].id + "'></span>";
}
// size
if (GM_getValue('printCacheTableS', true)) {
td = createElement('td', {
style : "border-bottom:1px solid lightgray;border-right:1px dashed lightgray;"
});
tr.appendChild(td);
td.innerHTML = "<span style='margin:0 2px' id='s_" + tour.geocaches[i].id + "'></span>";
}
// last 4 logs
if (GM_getValue('printCacheTableL4L', true)) {
td = createElement('td', {
style : "border-bottom:1px solid lightgray;white-space:nowrap;"
});
tr.appendChild(td);
td.innerHTML = "<canvas id='l4l_" + tour.geocaches[i].id + "' width='17' height='17' style='margin-left: 2px;position: relative;top: 2px;'/>";
}
// favorite points
if (GM_getValue('printCacheTableFav', true)) {
td = createElement('td', {
style : "text-align:left;border-bottom:1px solid lightgray;border-left:1px dashed lightgray;"
});
tr.appendChild(td);
td.innerHTML = "<span id='fp_" + tour.geocaches[i].id + "'></span>";
// favorite score
td = createElement('td', {
style : "text-align:right;border-bottom:1px solid lightgray;border-right:1px dashed lightgray;"
});
tr.appendChild(td);
td.innerHTML = "<span id='fs_" + tour.geocaches[i].id + "'></span>";
}
// coords
td = createElement('td', {
style : "border-bottom:1px solid lightgray;white-space:nowrap;"
});
tr.appendChild(td);
td.innerHTML = "<span style='margin:0 2px' id=" + coords + "</span>";
// found checkbox
if (GM_getValue('printCacheTableFound', true)) {
td = createElement('td');
tr.appendChild(td);
td.style.verticalAlign = "middle";
td.innerHTML = "<div style='margin-left:auto;margin-right:auto;width:10px;height:10px;border:1px solid lightgray;'>&nbsp;</div>";
}
// note
if (GM_getValue('printCacheTableNote', true)) {
td = createElement('td', {
style : "border-bottom:1px solid lightgray;"
});
tr.appendChild(td);
td.style.verticalAlign = "middle";
td.style.width = "100%";
td.innerHTML = "&nbsp;";
}
} else {
if (GM_getValue('addCustomMarkerToCacheTable', false) === false) { // custom marker in separate table
isCostumMarker = costumMarker;
}
}
}
// table of custom markers
if (isCostumMarker) {
tbody.innerHTML +=
'<tr> ' +
' <td colspan="11" style="border-bottom:1px solid lightgray;"><b>' + $.gctour.lang('printview.marker') + '</b></td> ' +
'</tr>';
for (i = 0; i < nCaches; ++i) {
costumMarker = (typeof(tour.geocaches[i].latitude) != "undefined");
if (costumMarker) {
tr = document.createElement('tr');
tbody.appendChild(tr);
td = document.createElement('td');
tr.appendChild(td);
td.innerHTML = "<b style='margin:0 10px'>" + (i + 1) + "</b>";
td = document.createElement('td');
tr.appendChild(td);
td.innerHTML = "<img src='" + tour.geocaches[i].image + "'>";
td = document.createElement('td');
tr.appendChild(td);
td.style.verticalAlign = "middle";
td.style.width = "30%";
td.colSpan = "9";
td.style.borderBottom = '1px solid lightgray';
td.innerHTML = tour.geocaches[i].name;
td.innerHTML += " - " + new LatLon(tour.geocaches[i].latitude, tour.geocaches[i].longitude).toString();
}
}
}
$(title).append($(coverTable));
var overview_map = createElement('div', {
id : "overview_map"
});
$(title).append($(overview_map));
}
var geocaches = [];
var costumMarkers = [];
var maxPrintLogs = parseInt(GM_getValue('maxPrintLogs', 3), 10);
// fetch all caches in parallel, not one by one
var promises = [];
for (i = 0; i < nCaches; i++) {
costumMarker = (typeof(tour.geocaches[i].latitude) != "undefined");
if (costumMarker) {
promises.push(tour.geocaches[i]);
updateProgressBar();
} else {
// retrieve at least 4 logs (if available) in order to display Last4Logs status, independent of maxPrintLogs parameter
promises.push(getGeocache(tour.geocaches[i].id, Math.max(4,maxPrintLogs)));
}
}
try {
var cache_objects = await Promise.all(promises);
for (i = 0; i < cache_objects.length; ++i) {
costumMarker = (typeof(tour.geocaches[i].latitude) != "undefined");
if (!costumMarker) {
var geocache = cache_objects[i], pmOnlyDiv;
if (geocache == "pm only") {
pmOnlyDiv = createElement('div');
pmOnlyDiv.setAttribute('class', 'cacheDetail');
pmOnlyDiv.innerHTML = "<b><img src='" + tour.geocaches[i].image + "'>" + tour.geocaches[i].name + " (" + tour.geocaches[i].id + ") is PM ONLY</b>";
body.appendChild(pmOnlyDiv);
body.appendChild(document.createElement('br'));
$(document).find("span#coords_" + tour.geocaches[i].id)
.html("PM ONLY");
} else if (geocache == 404) {
pmOnlyDiv = createElement('div');
pmOnlyDiv.setAttribute('class', 'cacheDetail');
pmOnlyDiv.innerHTML = "<b><img src='" + tour.geocaches[i].image + "'>" + tour.geocaches[i].name + " (" + tour.geocaches[i].id + ") has INVALID GC CODE</b>";
body.appendChild(pmOnlyDiv);
body.appendChild(document.createElement('br'));
$(document).find("span#coords_" + tour.geocaches[i].id)
.html("INVALID GC CODE");
} else {
// logs
var logs_div = createElement('div');
var logs = geocache.logs;
// if maxprintlogs <= -1, export all logs to the print overview
if (maxPrintLogs <= -1) {
maxPrintLogs = logs.length;
}
for (var log_i = 0; (log_i < logs.length && (log_i < maxPrintLogs)); log_i++) {
var log_div = createElement('div', {
style : "width:100%;page-break-inside:avoid;"
});
log_div.setAttribute("class", "removable");
var log_type_img = createElement('img', {
src : GS_HOST + 'images/logtypes/' + logs[log_i].LogTypeImage
});
log_div.appendChild(log_type_img);
log_div.innerHTML += " " + logs[log_i].Visited + " - " + logs[log_i].UserName + " (" + logs[log_i].GeocacheFindCount + ")<br/>";
log_div.innerHTML += logs[log_i].LogText;
log_div.style.borderBottom = "1px dashed lightgray";
append(log_div, logs_div);
}
var dummy_additional_waypoints = createElement('div');
if (GM_getValue('printAdditionalWaypoints', true)) {
var wpts_table = createElement('table', {
style : "width:100%;border-collapse:separate;"
});
append(wpts_table, dummy_additional_waypoints);
wpts_table.setAttribute("class", "removable");
var content = "";
for (var waypoints_i = 0; waypoints_i < geocache.additional_waypoints.length; waypoints_i++) {
if (waypoints_i % 2 === 0) {
content += "<tr>";
}
content += "<td style='width:50%;'>";
content += "<img src='" + geocache.additional_waypoints[waypoints_i].symbol + "'>&nbsp;";
content += "<b>" + geocache.additional_waypoints[waypoints_i].prefix + "&middot;</b>";
content += "<b>" + geocache.additional_waypoints[waypoints_i].name + "</b>";
content += " | " + geocache.additional_waypoints[waypoints_i].coordinates + "<br/>";
content += "<i>" + geocache.additional_waypoints[waypoints_i].note + "</i><br/>";
content += "</td>";
if (waypoints_i % 2 === 1 || waypoints_i === geocache.additional_waypoints.length-1) {
content += "</tr>";
}
}
wpts_table.innerHTML = content;
}
// images
var dummy_images = createElement('div');
if (GM_getValue('printSpoilerImages', true)) {
var image_table = createElement('table', {
style : "border-collapse:separate;border-spacing:2px;width:100%"
});
append(image_table, dummy_images);
content = "";
for (var images_i = 0; images_i < geocache.images.length; images_i++) {
if (images_i % 2 === 0) {
content += "<tr>";
}
content += "<td class='removable'>";
content += "<img style='max-width:8cm;' src='" + geocache.images[images_i].href + "'><br/>";
content += "<b>" + geocache.images[images_i].textContent + "</b>";
content += "</td>";
if (images_i % 2 === 1 || images_i === geocache.images.length-1) {
content += "</tr>";
}
}
image_table.innerHTML = content;
}
// inventory
var inventory = createElement('span');
for (var inventory_i = 0; inventory_i < geocache.$inventory.length; inventory_i++) {
var image = createElement('img');
image.src = geocache.$inventory[inventory_i].src;
append(image, inventory);
}
if (geocache.$inventory.length === 0) {
var empty_inventory = createElement('span');
empty_inventory.innerHTML = "empty";
append(empty_inventory, inventory);
}
// attributes
var attributes = createElement('span');
for (var attributes_i = 0; attributes_i < geocache.$attributes.length; attributes_i++) {
var attribute = geocache.$attributes[attributes_i];
attribute.style.width = "16px";
attribute.style.height = "16px";
attribute.style.marginRight = "3px";
if (attribute.src != $.gctour.img.attribute_blank) {
append(attribute, attributes);
}
}
var map_element_dummy = createElement('div');
var map_element = createElement('div');
append(map_element, map_element_dummy);
// map the geocache to uploadable version
var mapCache = {};
mapCache.gcid = geocache.gcid;
mapCache.guid = geocache.guid;
mapCache.image = geocache.image;
mapCache.name = geocache.name;
mapCache.difficulty = geocache.difficulty;
mapCache.terrain = geocache.terrain;
mapCache.latitude = geocache.lat;
mapCache.longitude = geocache.lon;
// add additional waypoints
var additional_waypoints = geocache.additional_waypoints;
for (waypoint_i = 0; waypoint_i < additional_waypoints.length; waypoint_i++) {
additional_waypoints[waypoint_i].note = "";
}
mapCache.additional_waypoints = additional_waypoints;
geocaches.push(mapCache);
// export from GCComment script
var gcComment = "";
if (geocache.comment) {
gcComment = "<b><u>GCComment:</u></b><br/>";
if (geocache.comment.lat) {
var parsedCoords = new LatLon(geocache.comment.lat, geocache.comment.lng).toString();
gcComment += "<b>Final Coordinates:</b> " + parsedCoords + "<br/>";
}
gcComment += "<b>Comment:</b> (" + geocache.comment.state + ") " + geocache.comment.commentValue;
}
// cache note from Groundspeak
var cache_note = "";
if (geocache.cache_note) {
cache_note = "<b><u>Cache Note:</u></b><br/>";
cache_note += '<span style="word-wrap:break-word;">' + geocache.cache_note + '</span>';
}
if (GM_getValue('printFrontpage', true) && !minimal) {
$(document)
// setting real coordinates on front page
.find("span#coords_" + geocache.gcid)
.html(geocache.coordinates)
.css({
'border-bottom' : (geocache.coordinatesisedit === true ? '2px solid gray' : 'none')
})
.end()
// setting D, T, size and favorite points on front page
.find("span#d_" + geocache.gcid).html(geocache.difficulty).end()
.find("span#t_" + geocache.gcid).html(geocache.terrain).end()
.find("span#s_" + geocache.gcid).html(geocache.size.substring(0, 1)).end()
.find("span#fp_" + geocache.gcid).html(geocache.fav).end()
.find("span#fs_" + geocache.gcid).html(geocache.favScore).end();
// set the last 4 logs icon:
if (GM_getValue('printCacheTableL4L', true)) {
getLast4Logs(geocache.logs, $("canvas#l4l_" + geocache.gcid, document));
}
}
geocache.name = escapeHTML(geocache.name);
var geocacheMapping = [
['GCID', geocache.gcid],
['CACHECOUNT', i + 1],
['GUID', geocache.guid],
['TYPE', geocache.type],
['CACHENAME', (geocache.available) ? geocache.name : "<span style='text-decoration: line-through !important;'>" + geocache.name + "</span>"],
['CACHESYM', geocache.cacheSym],
['OWNER', geocache.owner],
['HIDDEN', await formatDate(geocache.hidden)],
['ATTRIBUTES', attributes.innerHTML],
['BEARING', geocache.bearing],
['DISTANCE', geocache.distance],
['INVENTORY', inventory.innerHTML],
['COORDINATESISEDIT', (geocache.coordinatesisedit === true) ? '' : 'hidden'],
['COORDINATES', (geocache.coordinatesisedit === true) ? (geocache.coordinates + " (Original: " + geocache.coordinates_original + ")") : geocache.coordinates],
['DIFFICULTY', geocache.difficulty.replace(/\./, "_")],
['TERRAIN', geocache.terrain.replace(/\./, "_")],
['SIZE', geocache.size.toLowerCase().replace(/ /, "_")],
['SHORT_DESCRIPTION', (geocache.$short_description.length === 1) ? geocache.$short_description.html() : ""],
['LONG_DESCRIPTION', (geocache.$long_description.length === 1) ? geocache.$long_description.html() : ""],
['GCCOMMENT', gcComment],
['CACHENOTE', cache_note],
['HINT', (GM_getValue('decryptPrintHints', true)) ? geocache.hint : convertROTStringWithBrackets(geocache.hint)],
['ADDITIONAL_WAYPOINTS', dummy_additional_waypoints.innerHTML],
['IMAGES', dummy_images.innerHTML],
['MAP', map_element_dummy.innerHTML],
['MAPID', "MAP_" + geocache.gcid],
['LOGCOUNTER', (GM_getValue('printLoggedVisits', false) && geocache.find_count) ? geocache.find_count : ""], // empty for non-published caches
['LOGS', logs_div.innerHTML],
['FAV', geocache.fav ? geocache.fav : ''],
['FAVSCORE', geocache.favScore ? geocache.favScore : ''],
['HIDDENSTYLE', minimal ? 'hidden' : '']
];
var cacheDetailTemp = fillTemplate(geocacheMapping, cacheDetailTemplate);
// class "removable" elements and removable images in cache description
$(".removable, div.short img, div.long img", cacheDetailTemp)
.on('click', function(e) {
e.stopPropagation();
$(this).remove();
})
.on("mouseenter", function () {
$(this).css({
"opacity": "0.5",
"cursor": "url('" + $.gctour.img.del + "'),pointer"
});
})
.on("mouseleave", function () {
$(this).css({
"opacity": 1
});
});
// remove href attribute from links in cache description
$("div.short a, div.long a", cacheDetailTemp).removeAttr("href");
// add edit mode
if (GM_getValue('printEditMode', true)) {
$("div.short, div.long", cacheDetailTemp).attr('contenteditable', 'true');
}
if (GM_getValue('printPageBreak', false)) {
if (i < nCaches - 1) {
cacheDetailTemp.style.pageBreakAfter = 'always';
}
}
body.appendChild(cacheDetailTemp);
body.appendChild(document.createElement('br'));
}
} else {
// map custom marker to uploadable version
var cm = cache_objects[i];
cm.index = i;
costumMarkers.push(cm);
var markerMapping = [
['GCID', $.gctour.lang('printview.marker')],
['CACHECOUNT', (i + 1)],
['TYPE', tour.geocaches[i].image],
['NAME', tour.geocaches[i].name],
['COORDINATES', new LatLon(tour.geocaches[i].latitude, tour.geocaches[i].longitude).toString()],
['CONTENT', tour.geocaches[i].content.replace(/\n/g, "<br />")],
['HIDDENSTYLE', minimal ? 'hidden' : '']
];
cacheDetailTemp = fillTemplate(markerMapping, ownMarkerTemplate);
body.appendChild(cacheDetailTemp);
body.appendChild(document.createElement('br'));
}
}
closeOverlayRemote(document)(); // close old overlay (scraping data)
// maps
addProgressbar({
caption : $.gctour.lang('container.tourHeader.makeMap.wait'),
_document : document,
closeCallback : function(_document) {
return function() {
if (confirm($.gctour.lang('general.cancel') + "?") == true) {
window.location.reload();
}
};
}
});
// upload map content to server
var cacheObject = {};
cacheObject.geocaches = geocaches;
cacheObject.costumMarkers = costumMarkers;
uploadMap(cacheObject);
// show maps
try {
var overviewMapQuery = "";
var geocacheCodes = [];
for (i = 0; i < nCaches; ++i) {
var marker = tour.geocaches[i];
if (marker.wptcode) {
overviewMapQuery += marker.wptcode;
} else {
overviewMapQuery += (marker.id) ? marker.id : marker.gcid;
geocacheCodes.push((marker.id) ? marker.id : marker.gcid);
}
if (i < nCaches - 1) {
overviewMapQuery += ",";
}
}
// overview map
var printOverviewMap = (
GM_getValue('printOutlineMap', true) &&
GM_getValue('printFrontpage', true) &&
!minimal);
var mapCount = (printOverviewMap) ? 1 : 0;
mapCount += (GM_getValue('printOutlineMapSingle', false)) ? geocacheCodes.length : 0;
if (printOverviewMap) {
$("div#overview_map", document).first().append(await getMapElement(overviewMapQuery, document));
setProgress(0, mapCount, document);
}
// map for each geocache
var printMapForEachCache = (
GM_getValue('showCacheDetails', true) &&
GM_getValue('printOutlineMapSingle', false));
if (printMapForEachCache) {
for (i = 0; i < geocacheCodes.length; ++i) {
var geocacheCode = geocacheCodes[i];
var mapElement = $("div#MAP_" + geocacheCode, document).first();
if (mapElement) {
mapElement.append(await getMapElement(geocacheCode, document));
}
setProgress(i + 1, mapCount, document);
}
}
closeOverlayRemote(document)();
} catch (e) {
throw "show maps - " + e;
}
}
catch(e) {
addErrorDialog({
caption : "printPageFunction error",
_document : document,
_exception : e
});
}
// scale listing images to fit print preview
$('div.short img, div.long img').removeAttr('width').removeAttr('height').removeAttr('style').css({'max-width':'100%','max-height':'100%','object-fit':'contain'});
}
// funktion ähnlich http://www.gsak.net/help/hs11980.htm
function getLast4Logs(logs, canvas_element) {
var getColor = function(log4Logs) {
if ((typeof(log4Logs)) === 'undefined') {
return "LightGray";
}
switch (log4Logs.LogType) {
case "Found it":
return "green";
case "Didn't find it":
return "red";
case "Needs Maintenance":
return "blue";
case "Temporarily Disable Listing":
return "black";
case "Needs Archived":
return "yellow";
default:
return "LightGray";
}
};
var ctx = canvas_element.get(0).getContext('2d');
ctx.fillStyle = "black";
ctx.clearRect(1, 1, 15, 15);
var pos = [[2, 2], [9, 2], [2, 9], [9, 9]];
var dim = [6, 6];
for (var i = 0; i < pos.length; i++) {
ctx.fillStyle = getColor(logs[i]);
ctx.fillRect(pos[i][0], pos[i][1], dim[0], dim[1]);
}
}
/* printpage maps */
function updateMapSize(newDocument, mapId, factor) {
return function() {
var map = newDocument.getElementById(mapId).getElementsByTagName('iframe')[0];
map.style.width = (factor * 20) + "cm";
map.style.height = "500px";
};
}
function getMapType() {
return GM_getValue('printOutlineMapType', 'roadmap');
}
function getMapSettings() {
var settings = [];
// settings String:
// 1 - Geocache GCID
// 2 - Geocache Name
// 3 - Waypoint Hide all
// 4 - Waypoint Name
// 5 - Waypoint Lookup
// 6 - Own Waypoint show
// 7 - Own Waypoints name
// 8 - Show gc.de maps overlay
// 9 - Show Geocache Index
settings.push(GM_getValue('settings_map_geocacheid', true));
settings.push(GM_getValue('settings_map_geocachename', true));
settings.push(GM_getValue('settings_map_awpts', true));
settings.push(GM_getValue('settings_map_awpt_name', true));
settings.push(GM_getValue('settings_map_awpt_lookup', true));
settings.push(GM_getValue('settings_map_owpts', true));
settings.push(GM_getValue('settings_map_owpt_name', true));
settings.push(false); // gc.de maps does not exist anymore: do not skip here, but set to false!
settings.push(GM_getValue('settings_map_geocacheindex', true));
return settings.join("").replace(/true/g, "1").replace(/false/g, "0");
}
async function getMapUrl(mapQuery) {
try {
var headers = { 'Content-type': 'application/x-www-form-urlencoded' },
response = await promiseRequest('POST', GCTOUR_HOST + "map/make", headers, encodeURI("ids=" + mapQuery)),
hash_value = response.responseText;
debug("Hash '" + hash_value + "' for this query '" + mapQuery + "'");
return GCTOUR_HOST + "map/show/h/" + hash_value + "/" + getMapSettings() + "/" + getMapType();
} catch(e) {
throw 'fn getMapUrl - ' + e;
}
}
async function getMap(mapQuery) {
var map_size_px,
mapId = mapQuery.replace(/,/g, ""),
map_frame = document.createElement('iframe');
switch (GM_getValue('defaultMapSize', 'large')) {
case "medium":
map_size_px = 375;
break;
case "small":
map_size_px = 250;
break;
default:
map_size_px = 500;
break;
}
map_frame.className = 'cacheMap';
map_frame.id = mapId;
map_frame.style.width = "100%";
map_frame.style.height = map_size_px + 'px';
$(map_frame).css("border", '1px solid lightgray');
$(map_frame).css("box-sizing", "border-box");
map_frame.src = await getMapUrl(mapQuery);
return map_frame;
}
async function getMapControl(mapQuery, map_frame, newDocument) {
var control_container = createElement('div'),
map_size_px;
control_container.className = 'noprint';
switch (GM_getValue('defaultMapSize', 'large')) {
case "small":
map_size_px = 250;
break;
case "medium":
map_size_px = 375;
break;
default:
map_size_px = 500;
break;
}
$(control_container).append(
$('<div>'+$.gctour.lang('printview.mapHeight')+'</div>')
.css('float', 'left')
.css('margin-right', '5px'),
$("<div/>").gct_slider({
min : 100,
max : 1000,
value : map_size_px,
document : newDocument,
slide : function(values) {
map_frame.style.height = values.value + "px";
}
})
.css('float', 'left')
.css('width', '250px'),
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.del,
title : $.gctour.lang('printview.removeMap'),
alt : $.gctour.lang('printview.removeMap')
}).on('click', function () {
map_frame.parentNode.style.display = "none";
})
.css('float', 'right'),
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.refresh,
title : $.gctour.lang('printview.reloadMap'),
alt: $.gctour.lang('printview.reloadMap')
}).on('click', function () {
map_frame.src = map_frame.src; // necessary to reload map
})
.css('float', 'right'),
$('<img>', {
'class' : 'tourImage',
src : $.gctour.img.map,
title : $.gctour.lang('printview.zoomMap'),
alt: $.gctour.lang('printview.zoomMap')
}).on('click', async function () {
GM_openInTab(await getMapUrl(mapQuery), false);
})
.css('float', 'right'),
$('<div/>')
.css('clear', 'both')).find("img.tourImage").addShadowEffect().addOpacityEffect().css('margin-left', '5px');
return control_container;
}
// add gct_slider method to jquery (e.g. height slider in print preview)
(function($) {
var methods = {
init : function(options) {
var settings = $.extend({
'min' : '0',
'max' : '100',
'document' : document,
'value' : 0
}, options),
scroller_element = $("<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>")
.appendTo(this),
dragged = false,
slider_width = 0,
slider_offset = 0,
percentage = 0,
self = this;
// set start value
percentage = (100 * settings.value) / settings.max;
scroller_element
.css("left", (percentage) + "%")
.on('click', function(event) {
event.preventDefault();
})
.on("mouseenter", function () {
$(this).addClass("ui-state-hover");
})
.on("mouseleave", function () {
$(this).removeClass("ui-state-hover");
})
.on('focus', function() {
$(".ui-slider .ui-state-focus").removeClass("ui-state-focus");
$(this).addClass("ui-state-focus");
})
.on('blur', function() {
$(this).removeClass("ui-state-focus");
})
.on('mousedown', function(e) {
e.preventDefault();
dragged = true;
slider_width = parseInt(self.css("width"), 10);
slider_offset = parseInt(self.offset().left, 10);
scroller_element.addClass("ui-state-active");
methods["trigger"].apply(self, ["start", methods["calculate"].apply(self, [percentage])]);
});
this.addClass('ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all')
.append(scroller_element);
$('*', settings.document).on('mousemove', function(e) {
if (dragged) {
e.preventDefault();
percentage = (100 * (e.pageX - slider_offset)) / (slider_width);
percentage = (percentage < 0) ? 0 : percentage;
percentage = (percentage > 100) ? 100 : percentage;
// debug("MousePos:"+e.pageX+"\tSliderWidth:"+slider_width+"\tSliderOffset:"+slider_offset+"\tMove to:"+percentage);
scroller_element.css("left", (percentage) + "%");
methods["trigger"].apply(self, ["slide", methods["calculate"].apply(self, [percentage])]);
}
});
$('*', settings.document).on('mouseup', function () {
if (dragged) {
dragged = false;
scroller_element.removeClass("ui-state-active");
methods["trigger"].apply(self, ["stop", methods["calculate"].apply(self, [percentage])]);
}
});
this.data('gct_slider', {
target : $(this),
settings : settings
});
return this;
},
calculate : function(percentage) {
var $data = $(this).data('gct_slider'),
max = $data.settings.max,
min = $data.settings.min,
relative_value = (percentage * (max - min)) / 100,
value = min + relative_value;
// log("relative_value"+ relative_value+ "\tvalue:"+value);
return {
percentage : percentage,
value : value
};
},
trigger : function(type, data) {
var $data = $(this).data('gct_slider'),
callback = $data.settings[type],
data = data || {};
return !(typeof callback === "function" &&
callback.apply(this, [data]) === false);
}
};
$.fn.gct_slider = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.tooltip');
}
};
})($);
async function getMapElement(mapQuery, newDocument) {
var map_container = createElement('div', {
style : "text-align: center; margin-left: auto; margin-right: auto;"
});
var map_frame = await getMap(mapQuery);
map_container.appendChild(await getMapControl(mapQuery, map_frame, newDocument));
map_container.appendChild(map_frame);
return map_container;
}
/* cache scraper */
// source of all evil asking groundspeak
async function getGeocacheFromElement(element, maxLogsCount) {
var coordinates,
minimal_geocache,
$divCacheDetails,
latlonhref,
geocache = {};
if ($("div.LocationData", element).length === 0 || $("ul.premium-features-list", element).length > 0) {
return "pm only";
}
minimal_geocache = getMinimalGeocacheDetails(element);
geocache.gcid = minimal_geocache.gccode;
geocache.cacheid = minimal_geocache.cacheid;
geocache.guid = minimal_geocache.guid;
geocache.name = minimal_geocache.name;
geocache.type = minimal_geocache.type.split(".")[0];
geocache.image = GS_HOST + "images/wpttypes/" + geocache.type + ".gif";
geocache.sym = "Geocache";
let $logTypeImage = $('img#ctl00_ContentBody_GeoNav_logTypeImage', element);
try {
if ($logTypeImage.length > 0 && $logTypeImage.attr('src').split('/').pop().split('.')[0] === "2") {
geocache.sym = "Geocache Found";
}
} catch (e) {
error('src attribute missing - ' + e);
}
let owner = [
$('a[href*="/play/search?owner[0]="]', element).prop('href')?.split('owner[0]=')[1]?.split('&a=')[0],
$('a[href*="/play/search?fb="]', element).prop('href')?.split('fb=')[1]?.split('&a=')[0] // fallback
];
geocache.owner =
(owner[0] && decodeURIComponent(owner[0].trim())) ||
(owner[1] && decodeURIComponent(owner[1].trim())) ||
"";
let placed_by = [
$('a[href*="' + GS_HOST + 'p/"]', element).first().text()
];
geocache.placed_by =
(placed_by[0] && placed_by[0].trim()) ||
"";
if (unsafeWindow.getGCComment) {
var comment = unsafeWindow.getGCComment(geocache.guid);
if (comment) {
geocache.comment = comment;
}
}
var usernote = $("div#srOnlyCacheNote", element);
if (usernote.length > 0) {
geocache.cache_note = usernote.html();
}
// check availability
var warning_element = $("ul.OldWarning", element).first(); // contains text like
//This cache is temporarily unavailable. Read the logs below to read the status for this cache.
//This cache has been archived, but is available for viewing for archival purposes.
if (warning_element.length > 0) {
geocache.archived = (warning_element.text().indexOf("archived") != -1);
geocache.available = false;
} else {
geocache.archived = false;
geocache.available = true;
}
$divCacheDetails = $('#ctl00_ContentBody_mcd2', element).first();
geocache.hidden = await parseDate($divCacheDetails.text().split(':').pop().trim());
geocache.difficulty = $("span#ctl00_ContentBody_uxLegendScale > img", element).first().attr("alt").split(" out of ")[0].trim();
geocache.terrain = $("span#ctl00_ContentBody_Localize12 > img", element).first().attr("alt").split(" out of ")[0].trim();
geocache.size = $('img[src*="/images/icons/container/"]', element).first().attr("src").split("/")[4].split(".")[0].trim();
geocache.coordinates = $('span#uxLatLon', element).first().text();
// get userDefinedCoords from GS Javascript
// example string: var userDefinedCoords = {"status":"success","data":{"isUserDefined":false,"oldLatLngDisplay":"N 52° 31.268' E 013° 21.255'"}};
var patt = /var userDefinedCoords = {*([\s\S]*?)}[;]+/; // search for "var userDefinedCoords = {" until "};"
var r;
var userDefinedCoordsString = ((r = patt.exec($(element).text())) != null) ? r[1] : "";
var userDefinedCoords = JSON.parse('{' + userDefinedCoordsString + '}');
geocache.coordinatesisedit = (userDefinedCoords?.status == "success" && userDefinedCoords?.data?.isUserDefined); // false = original
geocache.coordinates_original = userDefinedCoords?.data?.oldLatLngDisplay ? userDefinedCoords.data.oldLatLngDisplay.replaceAll("'","") : "";
// get cache coordinates
latlonhref = $("span#ctl00_ContentBody_MapLinks_MapLinks a", element).map(function(i,el) {
return $(el).attr('href');
}).get();
geocache.lat = 0.0;
geocache.lon = 0.0;
for (let i = 0; i < latlonhref.length; ++i) {
if (latlonhref[i].includes("geocaching")) {
geocache.lat = latlonhref[i].split("lat=")[1].split("&")[0];
geocache.lon = latlonhref[i].split("lng=")[1];
break;
} else if (latlonhref[i].includes("mapquest")) {
geocache.lat = latlonhref[i].split("latitude=")[1].split("&")[0];
geocache.lon = latlonhref[i].split("longitude=")[1].split("&")[0];
break;
} else if (latlonhref[i].includes("bing")) {
geocache.lat = latlonhref[i].split("point")[1].split("_")[0];
geocache.lon = latlonhref[i].split("point")[1].split("_")[1];
break;
} else if (latlonhref[i].includes("opencyclemap")) {
geocache.lat = latlonhref[i].split("lat=")[1].split("&")[0];
geocache.lon = latlonhref[i].split("lon=")[1];
break;
} else if (latlonhref[i].includes("openstreetmap")) {
geocache.lat = latlonhref[i].split("mlat=")[1].split("&")[0];
geocache.lon = latlonhref[i].split("mlon=")[1].split("&")[0];
break;
}
}
// if the user moved the coordinates of this geocache by GCTour
if (GM_getValue('coords_' + geocache.gcid, "null") != "null") { // use it
coordinates = GM_getValue('coords_' + geocache.gcid, "null").split("#");
geocache.lat = coordinates[0];
geocache.lon = coordinates[1];
geocache.coordinates = new LatLon(geocache.lat, geocache.lon).toString();
geocache.coordinatesisedit = true;
}
geocache.location = $("span#ctl00_ContentBody_Location", element).first().text();
try {
// get the country and (if exists) the state!
if (geocache.location.indexOf(",") < 0) { // if the index of "," < 0 then the state is not given!
geocache.state = "";
geocache.country = geocache.location.split("In ")[1].trim();
} else {
geocache.state = geocache.location.split("In ")[1].split(',')[0].trim();
geocache.country = geocache.location.split("In ")[1].split(',')[1].trim();
}
} catch (e) { // something went wrong - write the whole location to country
geocache.state = "";
geocache.country = geocache.location.trim();
}
try {
geocache.bearing = $('span#lblDistFromHome > img', element).first().attr("alt");
var distanceTemp = $('span#lblDistFromHome', element).first().text().split(" ");
geocache.distance = (distanceTemp[0] + " " + distanceTemp[1]).trim();
} catch (e) {
// if homecoordinates are not set
geocache.bearing = "";
geocache.distance = "";
}
geocache.$inventory = $('span#ctl00_ContentBody_uxTravelBugList_uxInventoryLabel', element).parent().parent().find('img');
geocache.$attributes = $('div#ctl00_ContentBody_detailWidget > div.WidgetBody > img', element);
geocache.attributes_array = [];
geocache.$attributes.each(function() {
// remove garbage from source address and split at "-"
// (special care for "Special Tool Required" since the image name is "s-tool")
var attribute_array = this.src.replace(GS_HOST + "images/attributes/", "").replace(".png", "").replace("-y", ";y").replace("-n", ";n").split(";");
// iterate over every attribute defined in the global attributes array
for (var attributesDef_i = 0; attributesDef_i < ATTRIBUTES_ARRAY.length; attributesDef_i++) {
// ... and check whether the image is equal to the definition
if (attribute_array[0] == ATTRIBUTES_ARRAY[attributesDef_i][1]) {
// add this attribute as array with id-0, image-1, name-2 and yes/no-4
geocache.attributes_array.push([ATTRIBUTES_ARRAY[attributesDef_i][0], ATTRIBUTES_ARRAY[attributesDef_i][1], ATTRIBUTES_ARRAY[attributesDef_i][2], ((attribute_array[1] == "yes") ? 1 : 0)]);
}
}
});
geocache.$short_description = $('span#ctl00_ContentBody_ShortDescription', element).first();
geocache.$long_description = $('span#ctl00_ContentBody_LongDescription', element).first();
geocache.images = $('a[rel="lightbox"]', element);
geocache.additional_waypoints = [];
var additional_waypoints = $('table.Table > tbody > tr', element);
let wpt_id = geocache.cacheid * 100; // create a unique id for waypoints based on cacheid (append 2 digits)
for (var i = 0; i < additional_waypoints.length; i = i + 2) {
var row1 = additional_waypoints[i];
var row2 = additional_waypoints[i + 1];
var row1_tds = row1.getElementsByTagName('td');
var row2_tds = row2.getElementsByTagName('td');
var waypoint = {};
waypoint.symbol = row1_tds[1].childNodes[1].src;
waypoint.prefix = row1_tds[2].textContent.trim();
waypoint.lookup = row1_tds[3].textContent.trim();
waypoint.name = row1_tds[4].childNodes[1].textContent;
waypoint.url = row1_tds[4].childNodes[1].href.split('&')[0];
waypoint.coordinates = row1_tds[5].textContent.trim();
coordinates = await parseCoordinates(row1_tds[5].textContent);
waypoint.latitude = coordinates._lat;
waypoint.longitude = coordinates._lon;
waypoint.note = row2_tds[2].innerHTML.trim(); // HTML in waypoint description
waypoint.id = ++wpt_id;
// Final Location https://www.geocaching.com/images/WptTypes/sm/flag.jpg
// Parking Area https://www.geocaching.com/images/WptTypes/sm/pkg.jpg
// Question to Answer https://www.geocaching.com/images/WptTypes/sm/puzzle.jpg
// Stages of a Multicache https://www.geocaching.com/images/WptTypes/sm/stage.jpg
// Trailhead https://www.geocaching.com/images/WptTypes/sm/trailhead.jpg
// Reference Point https://www.geocaching.com/images/WptTypes/sm/waypoint.jpg
var sym = waypoint.symbol.toLowerCase().split(GS_HOST + GS_WPT_IMAGE_PATH)[1];
switch (sym) {
case "flag.jpg":
waypoint.symbol_groundspeak = "Final Location";
waypoint.type_groundspeak = "Waypoint|Final Location";
break;
case "pkg.jpg":
waypoint.symbol_groundspeak = "Parking Area";
waypoint.type_groundspeak = "Waypoint|Parking Area";
break;
case "puzzle.jpg":
waypoint.symbol_groundspeak = "Question to Answer";
waypoint.type_groundspeak = "Waypoint|Question to Answer";
break;
case "stage.jpg":
waypoint.symbol_groundspeak = "Stages of a Multicache";
waypoint.type_groundspeak = "Waypoint|Stages of a Multicache";
break;
case "trailhead.jpg":
waypoint.symbol_groundspeak = "Trailhead";
waypoint.type_groundspeak = "Waypoint|Trailhead";
break;
case "waypoint.jpg":
waypoint.symbol_groundspeak = "Reference Point";
waypoint.type_groundspeak = "Waypoint|Reference Point";
break;
default:
waypoint.symbol_groundspeak = "Unknown Type";
waypoint.type_groundspeak = "Waypoint|Unknown Type";
break;
}
geocache.additional_waypoints.push(waypoint);
}
var hints_element = $('div#div_hint', element).first();
geocache.hint = (hints_element.length > 0) ? convertROTStringWithBrackets(hints_element.html().trim()) : "";
// numbers of finds, DNFs, notes etc.
$('[aria-labelledby="LoggedVisits"]', element).css({ 'list-style-type': 'none', 'padding': '0' }).find('li').css({ 'display': 'inline-block', 'margin-right': '1rem' });
geocache.find_count = $('[aria-labelledby="LoggedVisits"]', element).prop('outerHTML');
// total number of logged visits (e.g. "8,507 Logged Visits"; order can vary for different languages)
var nLoggedVisits = parseInt( $('div.InformationWidget.Clear', element).children().first().text().replace(/[^0-9]/g,'') );
// favorite points
geocache.fav = $('span.favorite-value', element).text().trim();
// user token for fetching logs and favorite score
var userToken = element.innerHTML.split("userToken = '")[1].split("'")[0];
try {
// fetch logs
geocache.logs = await getLogs(userToken, maxLogsCount, nLoggedVisits, element);
// fav score only if favorite points are necessary and available (e.g. not for events)
if (FAVSCORE && geocache.fav) {
// fetch favorite score
try {
let url = GS_WEB_API + 'geocache/' + geocache.gcid + '/favoritepoints/score';
let method = "GET";
var response = await promiseRequest(method, url);
} catch (e) { // 500 error from GS
response = {};
response.responseText = 'ERROR';
}
// check for a string "between" 0 and 100, thereby exclude that content is a web page
if (response.responseText.length<4) {
var favScore = response.responseText;
if (favScore>100) {
favScore = '(100%)';
} else if (favScore<1) {
favScore = '(<1%)';
} else {
favScore = '('+favScore+'%)';
}
geocache.favScore = favScore;
} else {
geocache.favScore = '';
}
}
//debug('getGeocacheFromElement -\n' + JSON.stringify(geocache));
return geocache;
}
catch(e) {
throw "fn getGeocacheFromElement - " + e;
}
}
async function getGeocache(gcid, maxLogsCount) {
try {
var response = await promiseRequest('GET', GS_HOST + 'geocache/' + gcid, undefined, undefined, true);
// after execution parse the result
var response_div = createElement('div');
response_div.innerHTML = response.responseText;
return await getGeocacheFromElement(response_div, maxLogsCount);
} catch(e) {
if (e.indexOf('404') > 0) return 404; // invalid GC code
throw "fn getGeocache for " + gcid + " - " + e;
}
}
/* cache scraper utilities */
// return an object with these attributes: gccode, cacheid, name, guid, type
function getMinimalGeocacheDetails(detailsPage) {
var geocache_details = {};
var $obj = {}; // temp jquery container
/* GCCode
* <span id="ctl00_ContentBody_CoordInfoLinkControl1_uxCoordInfoCode" class="CoordInfoCode">GC2HFRB</span>
* <title>GC2HFRB 3, 2, 1 ... Lift-Off (Traditional Cache) in Thüringen, Germany created by Astronaut Hoffi1986</title>
*/
$obj.gcc = [
$('.CoordInfoCode', detailsPage).first().text(),
$('title', detailsPage).first().text()
];
geocache_details.gccode =
(findGCCodeFromString($obj.gcc[0])) ||
(findGCCodeFromString($obj.gcc[1])) ||
null;
if (!geocache_details.gccode) {
throw "fn getMinimalGeocacheDetails - Error getting GCCode!";
} else {
debug(
"getMinimalGeocacheDetails - GCCode: " + geocache_details.gccode + "\n" +
"\t1: " + findGCCodeFromString($obj.gcc[0]) + "\n" +
"\t2: " + findGCCodeFromString($obj.gcc[1]));
}
/* CacheId:
* HTML REGEXP Quelle
* ccid=1957539" ccid=(\d+) "View all Trackables" Link
* "CacheID":1957539 \"CacheID\":(\d+) teil der vorgeladenen Logs
* w=1957539" \Ww=(\d+) "Watch Listing" link
* log.aspx?id=1957539&LogType= log\.aspx\?id=\d+ "Archive Listing" link (owned listings)
*/
try {
var cacheid_regex = /ccid=\d+|\"CacheID\":\d+|\Ww=\d+|log\.aspx\?id=\d+/;
var cacheid_arr = cacheid_regex.exec(detailsPage.innerHTML);
geocache_details.cacheid = cacheid_arr[0].split(/:|=/)[1];
debug("getMinimalGeocacheDetails - CacheID: " + geocache_details.cacheid);
} catch (e) {
throw "fn getMinimalGeocacheDetails - Error getting 'cacheid' from " + geocache_details.gccode;
}
/* Cachename:
* <span id="ctl00_ContentBody_CacheName">3, 2, 1 ... Lift-Off</span></h2>
* <meta name="og:title" content="3, 2, 1 ... Lift-Off" property="og:title" />
*/
$obj.name = [
$('span#ctl00_ContentBody_CacheName', detailsPage).first().text(),
$('meta[name="og:title"]', detailsPage).first().attr('content') // Fallback #1
];
geocache_details.name =
($obj.name[0] && $obj.name[0].trim()) ||
($obj.name[1] && $obj.name[1].trim()) ||
null;
if (!geocache_details.name) {
throw "fn getMinimalGeocacheDetails - Error getting 'cacheName' from " + geocache_details.gccode;
} else {
debug(
"getMinimalGeocacheDetails - Name: " + geocache_details.name + "\n" +
"\t1: " + (($obj.name[0]) ? $obj.name[0] : "null") + "\n" +
"\t2: " + (($obj.name[1]) ? $obj.name[1] : "null"));
}
/* guid
* <form method="post" action="/geocache/GC2HFRB_3-2-1-lift-off?guid=712fed16-77ab-48f4-a269-18cc27bb2a14" id="aspnetForm">
* <a id="ctl00_ContentBody_lnkPrintFriendly5Logs" href="cdpf.aspx?guid=712fed16-77ab-48f4-a269-18cc27bb2a14&amp;lc=5" target="_blank">5 Logs</a>&nbsp;
* <a id="ctl00_ContentBody_uxTravelBugList_uxTrackableItemsHistory" href="../track/search.aspx?wid=712fed16-77ab-48f4-a269-18cc27bb2a14">View past Trackables</a>
* <a id="ctl00_ContentBody_uxLogbookLink" href="cache_logbook.aspx?guid=712fed16-77ab-48f4-a269-18cc27bb2a14">View Logbook</a>
* <a href="/seek/gallery.aspx?guid=712fed16-77ab-48f4-a269-18cc27bb2a14">View Gallery</a>
*/
$obj.guid = [
$("form[id='aspnetForm'][action*='guid=']", detailsPage).first().attr("action"),
$("a#ctl00_ContentBody_lnkPrintFriendly[href*='guid=']", detailsPage).first().attr("href"),
$("a#ctl00_ContentBody_uxTravelBugList_uxTrackableItemsHistory[href*='wid=']", detailsPage).first().attr("href"),
$("a#ctl00_ContentBody_uxLogbookLink[href*='guid=']", detailsPage).first().attr("href"),
$("div.CacheDetailNavigation a[href*='guid=']", detailsPage).first().attr("href")
];
geocache_details.guid =
($obj.guid[0] && $obj.guid[0].split("guid=")[1].trim()) ||
($obj.guid[1] && $obj.guid[1].split("guid=")[1].trim()) ||
($obj.guid[2] && $obj.guid[2].split("wid=")[1].trim()) ||
($obj.guid[3] && $obj.guid[3].split("guid=")[1].trim()) ||
($obj.guid[4] && $obj.guid[4].split("guid=")[1].trim())
null;
if (!geocache_details.guid) {
throw "fn getMinimalGeocacheDetails - Error getting 'guid' from " + geocache_details.gccode;
} else {
debug(
"getMinimalGeocacheDetails - Guid: " + geocache_details.guid + "\n" +
"\t1: " + (($obj.guid[0]) ? $obj.guid[0].split("guid=")[1].split("&")[0] : "null") + "\n" +
"\t2: " + (($obj.guid[1]) ? $obj.guid[1].split("guid=")[1].split("&")[0] : "null") + "\n" +
"\t3: " + (($obj.guid[2]) ? $obj.guid[2].split("wid=")[1].split("&")[0] : "null") + "\n" +
"\t4: " + (($obj.guid[3]) ? $obj.guid[3].split("guid=")[1].split("&")[0] : "null") + "\n" +
"\t5: " + (($obj.guid[4]) ? $obj.guid[4].split("guid=")[1] : "null"));
}
/* type
* <use xlink:href="/app/ui-icons/sprites/cache-types.svg#icon-137-disabled"></use>
* <span class="btn-add-to-list" data-gcrefcode="GC3Y9WC" data-href="/bookmarks/mark.aspx?view=legacy&guid=e36a3706-8336-4071-b14e-d04537a4bfa0&amp;WptTypeID=137">Add to list</a>
* <a href="/bookmarks/ignore.aspx?guid=e36a3706-8336-4071-b14e-d04537a4bfa0&amp;WptTypeID=137">Ignore</a>
* // GCLH2 modification of add to ignore list button:
* <a href="javascript:void(0);" data-url="https://www.geocaching.com/bookmarks/ignore.aspx?guid=e36a3706-8336-4071-b14e-d04537a4bfa0&amp;WptTypeID=137" style="background-image: url(&quot;/images/icons/16/ignore.png&quot;);">Ignore</a>
*/
$obj.type = [
$('svg.cache-icon > use', detailsPage)[0], // starting at 2018-08-18
$('li#ctl00_ContentBody_GeoNav_uxAddToListBtn > span', detailsPage).attr('data-href'),
$('li#ctl00_ContentBody_GeoNav_uxIgnoreBtn > a[href*="ignore.aspx"]', detailsPage).attr("href"),
$('li#ctl00_ContentBody_GeoNav_uxIgnoreBtn > a[data-url*="ignore.aspx"]', detailsPage).attr("data-url")
];
geocache_details.type =
($obj.type[0] && $obj.type[0].href.baseVal.split("#")[1].split("-")[1] + ".gif") ||
($obj.type[1] && $obj.type[1].split("=")[3] + ".gif") ||
($obj.type[2] && $obj.type[2].split("=")[2] + ".gif") ||
($obj.type[3] && $obj.type[3].split("=")[2] + ".gif")
null;
if (!geocache_details.type) {
throw "fn getMinimalGeocacheDetails - Error getting 'type' from " + geocache_details.gccode;
} else {
debug(
"getMinimalGeocacheDetails - Type: " + geocache_details.type + "\n" +
"\t1: " + (($obj.type[0]) ? $obj.type[0].href.baseVal.split("#")[1].split("-")[1] + ".gif" : "null") + "\n" +
"\t2: " + (($obj.type[1]) ? $obj.type[1].split("=")[3] + ".gif" : "null") + "\n" +
"\t3: " + (($obj.type[2]) ? $obj.type[2].split("=")[2] + ".gif" : "null") + "\n" +
"\t4: " + (($obj.type[3]) ? $obj.type[3].split("=")[2] + ".gif" : "null"));
}
return geocache_details;
}
async function getLogs(userToken, maxLogsCount, nLoggedVisits, element) {
try {
maxLogsCount = Number.isInteger(maxLogsCount) ? maxLogsCount : 0;
nLoggedVisits = Number.isInteger(nLoggedVisits) ? nLoggedVisits : 0;
if (maxLogsCount == 0 || nLoggedVisits == 0) { // no logs
return [];
}
var nLogs = Math.min(maxLogsCount,nLoggedVisits), // number of required logs
logs = [],
fetch = true;
// up to 25 logs are already inside fetched listing, no need to fetch again
if (nLogs < 26) {
try {
var initialLogs = '{"' + element.innerHTML.split('initialLogs = {"')[1].split('};')[0] + '}';
logs = JSON.parse(initialLogs).data;
fetch = false;
} catch(e) { // if something went wrong give it a 2nd try and fetch the logs
error(e);
}
}
// fetch logs
if (fetch) {
var i,
// max possible number of logs by a single request: 100
nMax = 100,
nLogsByRequest = Math.min(nLogs,nMax),
// number of pages to be fetched
nPages = Math.ceil(nLogs/nLogsByRequest),
// idx: page index, num: number of logs by page (max:100)
// num=100,nLogs=200: --> idx=[1,2]: [1,100],[101,200]
// num=50, nLogs=200: --> idx=[1,2,3,4] [1,50],[51,100],[101,150],[151,200]
urlTemplate = GS_HOST + 'seek/geocache.logbook?tkn=' + userToken + '&idx=#PAGE#&num=' + nLogsByRequest + '&decrypt=false',
url,
log_obj = {},
log_objects,
promises = [];
// fetch all logs in parallel
for (i=1; i<=nPages; i++) {
url = urlTemplate.replace("#PAGE#", i);
promises.push(promiseRequest('GET', url));
}
log_objects = await Promise.all(promises);
for (i=0; i<log_objects.length; i++) {
// parse the result
log_obj = JSON.parse(log_objects[i].responseText);
// append new logs
logs = logs.concat(log_obj.data);
}
}
// truncate log array if necessary
if (logs.length > nLogs) {
logs = logs.slice(0, nLogs-logs.length);
}
return logs;
} catch(e) {
throw 'fn getLogs - ' + e;
}
}
/* gpx geocache */
function getAttributeXML(attribute_a) {
return ' <groundspeak:attribute id="' + attribute_a[0] + '" inc="' + attribute_a[3] + '">' + attribute_a[2] + '</groundspeak:attribute>\n';
}
function getGPXfromMarker(marker) {
var gpx = '';
gpx += ' <wpt lat="' + marker.latitude + '" lon="' + marker.longitude + '">\n';
gpx += ' <time>' + xsdDateTime(new Date()) + '</time>\n';
gpx += ' <name>' + escapeHTML(marker.name) + '</name>\n';
gpx += ' <cmt>' + escapeHTML(marker.content) + '</cmt>\n';
gpx += ' <sym>' + marker.symbol + '</sym>\n';
gpx += ' </wpt>';
return gpx;
}
function getWaypointsGPXFromGeocache(waypoint, geocache) {
if (GM_getValue('gpxignoreparkwpts', false)) {
// ignore parking waypoints
if (waypoint.symbol.includes('pkg.jpg') ||
waypoint.symbol_groundspeak.includes('Parking Area') ||
waypoint.type_groundspeak.includes('Parking Area')) {
return '';
}
}
if (GM_getValue('gpxignoreequallistingcoordswpts', false)) {
// ignore waypoints whose coords are equal to listing coords (up to 5 digits)
if (Math.round(waypoint.latitude * 100000) / 100000 === Number(geocache.latitude) &&
Math.round(waypoint.longitude * 100000) / 100000 === Number(geocache.longitude)) {
return '';
}
}
var gpx =
' <wpt lat="' + waypoint.latitude + '" lon="' + waypoint.longitude + '">\n' +
' <time>' + xsdDateTime(geocache.dateHidden) + '</time>\n' +
' <name>' + waypoint.prefix + geocache.gcid.replace(/GC/, '') + '</name>\n' +
' <cmt>' + escapeHTML(waypoint.note) + '</cmt>\n' +
' <desc>' + escapeHTML(waypoint.name) + '</desc>\n' +
' <url>' + waypoint.url + '</url>\n' +
' <urlname>' + escapeHTML(waypoint.name) + '</urlname>\n' +
' <sym>' + waypoint.symbol_groundspeak + '</sym>\n' +
' <type>' + waypoint.type_groundspeak + '</type>\n' +
' </wpt>';
if (GM_getValue('gpxwptasgeocache', false)) {
gpx =
' <wpt lat="' + waypoint.latitude + '" lon="' + waypoint.longitude + '">\n' +
' <time>' + xsdDateTime(geocache.dateHidden) + '</time>\n' +
' <name>' + waypoint.prefix + '-' + geocache.gcid + '</name>\n' +
' <desc>' + escapeHTML(waypoint.name) + '</desc>\n' +
' <url>' + waypoint.url + '</url>\n' +
' <urlname>' + escapeHTML(waypoint.name) + '</urlname>\n' +
' <sym>##CACHESYM##</sym>\n' +
' <type>Geocache|##TYPE##</type>\n' +
' <groundspeak:cache id="' + waypoint.id + '" available="##AVAILABLE##" archived="##ARCHIVED##" xmlns:groundspeak="http://www.groundspeak.com/cache/1/0/1">\n' +
' <groundspeak:name>' + waypoint.prefix + ' - ##CACHENAME##</groundspeak:name>\n' +
' <groundspeak:placed_by>##PLACEDBY##</groundspeak:placed_by>\n' +
' <groundspeak:type>##TYPE##</groundspeak:type>\n' +
' <groundspeak:long_description html="true">' + escapeHTML(waypoint.note) + '</groundspeak:long_description>\n' +
' </groundspeak:cache>\n' +
' </wpt>';
}
return gpx;
}
async function getGPXGeoCache(gcid) {
try {
var i, // for ()
geocache = {},
maxGPXLogs = parseInt(GM_getValue('maxGPXLogs', 10), 10),
geocache_obj = await getGeocache(gcid, maxGPXLogs);
if (geocache_obj === "pm only" || geocache_obj === 404) { // basic member and pm only cache or retracted
return geocache_obj;
}
geocache.gcid = geocache_obj.gcid;
if (GM_getValue('gpxstripgc', false)) {
geocache.gcid = geocache.gcid.replace(/GC/, '');
}
geocache.guid = geocache_obj.guid;
geocache.cacheid = geocache_obj.cacheid;
geocache.archived = (geocache_obj.archived) ? "true" : "false";
geocache.available = (geocache_obj.available) ? "true" : "false";
geocache.cacheName = geocache_obj.name;
geocache.cachePlacedBy = geocache_obj.placed_by;
geocache.cacheOwner = geocache_obj.owner;
geocache.cacheSym = geocache_obj.sym;
switch (geocache_obj.size) {
case "micro":
geocache.cacheSize = "Micro";
break;
case "small":
geocache.cacheSize = "Small";
break;
case "regular":
geocache.cacheSize = "Regular";
break;
case "large":
geocache.cacheSize = "Large";
break;
case "other":
geocache.cacheSize = "Other";
break;
case "not_chosen":
geocache.cacheSize = "Not chosen";
break;
case "virtual":
geocache.cacheSize = "Virtual";
break;
default:
geocache.cacheSize = "";
break;
}
i = WPT_ARRAY.findIndex(obj => obj.wptTypeId == geocache_obj.type);
geocache.cacheType = WPT_ARRAY[i].name;
geocache.attributes_array = geocache_obj.attributes_array;
geocache.difficulty = geocache_obj.difficulty;
geocache.terrain = geocache_obj.terrain;
var summary = geocache_obj.$short_description,
description = geocache_obj.$long_description;
if (GM_getValue('gpxhtml', true)) {
geocache.shortDescription = (summary.length === 1) ? summary.html() : "";
geocache.longDescription = (description.length === 1) ? description.html() : "";
} else {
geocache.shortDescription = (summary.length === 1) ? summary.text() : "";
geocache.longDescription = (description.length === 1) ? description.text() : "";
}
geocache.hint = geocache_obj.hint;
geocache.state = geocache_obj.state;
geocache.country = geocache_obj.country;
geocache.dateHidden = geocache_obj.hidden;
// logs
geocache.logs = [];
for (i = 0; i < geocache_obj.logs.length; i++) {
var logObj = {};
// cacherName: "user"
// type: "Found It", "Didn't find it", "Temporarily Disable Listing", "Write note", "Enable Listing",...
// foundDate: "August 18" oder "February 17, 2007"
// id: 12345679
// content: "Netter Log eintrag."
var gc_log = geocache_obj.logs[i];
logObj.cacherName = gc_log.UserName;
logObj.cacherID = gc_log.AccountID;
logObj.type = gc_log.LogType;
logObj.foundDate = await parseDate(gc_log.Visited);
logObj.id = gc_log.LogID;
// handling of HTML code in logs (since GS change to Markdown logs), e.g.
// <p> One, two &amp; three</p>
// in a log becomes
// &lt;p&gt; One, two &amp;amp; three&lt;/p&gt;
// to parse
var textarea = document.createElement("textarea");
// gc_log.LogText = &lt;p&gt; One, two &amp;amp; three&lt;/p&gt;
// unescaped = <p> One, two &amp; three</p>
var unescaped = $('<textarea>').html(gc_log.LogText).val(); // unescape HTML
// logObj.content = One, two & three
logObj.content = $(unescaped).text(); // no HTML in logs
geocache.logs.push(logObj);
}
geocache.additionalWaypoints = geocache_obj.additional_waypoints;
geocache.latitude = geocache_obj.lat;
geocache.longitude = geocache_obj.lon;
geocache.favScore = geocache_obj.favScore;
geocache.cache_note = geocache_obj.cache_note;
const split = geocache_obj.coordinates_original.split(' ');
geocache.latitude_original = Geo.parseDMS(split.slice(0,3).join(' '));
geocache.longitude_original = Geo.parseDMS(split.slice(3,6).join(' '));
geocache.coordinatesisedit = geocache_obj.coordinatesisedit;
return geocache;
} catch(e) {
throw 'fn getGPXGeoCache - ' + e;
}
}
async function getGPX() {
try {
var i, ii, iii; // for ()
var gpxHeader =
'<?xml version="1.0" encoding="utf-8"?>\n' +
'<gpx xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" creator="GCTour" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd" xmlns="http://www.topografix.com/GPX/1/0">\n' +
' <name>' + escapeHTML(CURRENT_TOUR.name) + '</name>\n' +
// do not change <desc> content, otherwise GSAK does not handle additional cache waypoints as child waypoints during GPX import
' <desc>This is an individual cache generated from Geocaching.com</desc>\n' +
' <author>GCTour ' + VERSION + '</author>\n' +
' <url>' + GS_HOST + '</url>\n' +
' <urlname>Geocaching - High Tech Treasure Hunting</urlname>\n' +
' <time>' + (new Date()).toISOString() + '</time>\n' +
' <bounds minlat="##MINLAT##" minlon="##MINLON##" maxlat="##MAXLAT##" maxlon="##MAXLON##" />\n' +
'##GEOCACHES##\n' +
'##WAYPOINTS##\n' +
'</gpx>';
var geocacheTemplate =
' <wpt lat="##LAT##" lon="##LON##">\n' +
' <time>##TIME##</time>\n' +
' <name>##GCID##</name>\n' +
' <desc>##CACHENAME## by ##OWNER##, ##TYPE## (##DIFFICULTY##/##TERRAIN##)</desc>\n' +
' <url>https://www.geocaching.com/geocache/##GCID##</url>\n' +
' <urlname>##CACHENAME##</urlname>\n' +
' <sym>##CACHESYM##</sym>\n' +
' <type>Geocache|##TYPE##</type>\n' +
' <groundspeak:cache id="##CACHEID##" available="##AVAILABLE##" archived="##ARCHIVED##" xmlns:groundspeak="http://www.groundspeak.com/cache/1/0/1">\n' +
' <groundspeak:name>##CACHENAME##</groundspeak:name>\n' +
' <groundspeak:placed_by>##PLACEDBY##</groundspeak:placed_by>\n' +
' <groundspeak:owner>##OWNER##</groundspeak:owner>\n' +
' <groundspeak:type>##TYPE##</groundspeak:type>\n' +
' <groundspeak:container>##CONTAINER##</groundspeak:container>\n' +
' <groundspeak:attributes>\n##ATTRIBUTES## </groundspeak:attributes>\n' +
' <groundspeak:difficulty>##DIFFICULTY##</groundspeak:difficulty>\n' +
' <groundspeak:terrain>##TERRAIN##</groundspeak:terrain>\n' +
' <groundspeak:country>##COUNTRY##</groundspeak:country>\n' +
' <groundspeak:state>##STATE##</groundspeak:state>\n' +
' <groundspeak:short_description html="true">##SUMMARY## </groundspeak:short_description>\n' +
' <groundspeak:long_description html="true">##DESCRIPTION## </groundspeak:long_description>\n' +
' <groundspeak:encoded_hints>##HINT##</groundspeak:encoded_hints>\n' +
' <groundspeak:logs>\n##LOGS##\n </groundspeak:logs>\n' +
' </groundspeak:cache>\n' +
'##GSAKWPTEXTENSIONS##' +
' </wpt>';
var geocacheLogTemplate =
' <groundspeak:log id="##LOGID##">\n' +
' <groundspeak:date>##TIME##</groundspeak:date>\n' +
' <groundspeak:type>##LOGTYPE##</groundspeak:type>\n' +
' <groundspeak:finder id="##CACHERID##">##CACHERNAME##</groundspeak:finder>\n' +
' <groundspeak:text encoded="false">##LOGTEXT##</groundspeak:text>\n' +
' </groundspeak:log>';
var gsakWptExtensionsTemplate =
' <gsak:wptExtension xmlns:gsak="http://www.gsak.net/xmlv1/6">\n' +
' <gsak:LatBeforeCorrect>##LATORIGINAL##</gsak:LatBeforeCorrect>\n' +
' <gsak:LonBeforeCorrect>##LONORIGINAL##</gsak:LonBeforeCorrect>\n' +
' <gsak:GcNote>##CACHENOTE##</gsak:GcNote>\n' +
' </gsak:wptExtension>';
var gcStrArray = [],
wptStrArray = [],
minLat,
minLon,
maxLat,
maxLon,
nCaches = CURRENT_TOUR.geocaches.length;
// for progress bar update
PROGRESS_BAR.progress = 0;
PROGRESS_BAR.total = nCaches;
// fetch all caches in parallel, not one by one
var promises = [];
for (i = 0; i < nCaches; i++) {
var costumMarker = (typeof (CURRENT_TOUR.geocaches[i].latitude) != "undefined");
if (costumMarker) {
wptStrArray.push(getGPXfromMarker(CURRENT_TOUR.geocaches[i]));
updateProgressBar();
} else {
promises.push(getGPXGeoCache(CURRENT_TOUR.geocaches[i].id));
}
}
// fav score only needed if prefix option in GPX settings is active
if (!GM_getValue('gpxprefixfavscore', false)) {
FAVSCORE = false;
}
var cache_objects = await Promise.all(promises);
FAVSCORE = true;
var pmo = "", retracted = "";
for (i = 0; i < cache_objects.length; i++) {
// if the cancel button is pressed...
if (GM_getValue("stopTask", false)) {
GM_setValue("stopTask", false);
return "canceled"; // ...then return!
}
var geocache = cache_objects[i];
if (geocache === "pm only") { // pm only: not available for basic members
pmo += "#" + (i + 1) + ": " + CURRENT_TOUR.geocaches[i].name + " (" + CURRENT_TOUR.geocaches[i].id + ")<br>";
} else if (geocache === 404) { // retracted
retracted += "#" + (i + 1) + ": " + CURRENT_TOUR.geocaches[i].name + " (" + CURRENT_TOUR.geocaches[i].id + ")<br>";
} else {
var logs = geocache.logs,
logsStringArray = [],
attributeLog,
attributeLogtext;
// create log with attributes!
if (GM_getValue('gpxattributestolog', false)) {
attributeLogtext = $.map(geocache.attributes_array, function (row) {
return row[2] + ": " + ((row[3] === 1) ? "yes" : "no");
}).join("\n");
attributeLog = geocacheLogTemplate
.replace('##LOGID##', geocache.cacheid)
.replace('##TIME##', xsdDateTime(new Date()))
.replace('##CACHERID##', "")
.replace('##CACHERNAME##', "GCTour")
.replace('##LOGTYPE##', "Write note")
.replace('##LOGTEXT##', attributeLogtext);
logsStringArray.push(attributeLog);
}
// for removing all emoji codes in logs
var emoji_ranges = [
'\ud83c[\udf00-\udfff]', // U+1F300 to U+1F3FF
'\ud83d[\udc00-\ude4f]', // U+1F400 to U+1F64F
'\ud83d[\ude80-\udeff]' // U+1F680 to U+1F6FF
];
for (ii = 0; ii < logs.length; ii++) {
var geocacheLogMapping = [
['LOGID', logs[ii].id],
['TIME', xsdDateTime(logs[ii].foundDate)],
['CACHERNAME', escapeHTML(logs[ii].cacherName)],
['CACHERID', logs[ii].cacherID],
['LOGTYPE', logs[ii].type],
// remove ASCII control codes and escape HTML entities
['LOGTEXT', escapeHTML(stripASCIIcodes(logs[ii].content))]
// for removing all emoji codes in logs
//['LOGTEXT', escapeHTML( stripASCIIcodes(logs[ii].content).replaceAll(emoji_ranges.join('|'),'') )]
];
var cacheWaypointLog = geocacheLogTemplate;
for (iii = 0; iii < geocacheLogMapping.length; iii++) {
cacheWaypointLog = cacheWaypointLog.replaceAll("##" + geocacheLogMapping[iii][0] + "##", geocacheLogMapping[iii][1]);
}
logsStringArray.push(cacheWaypointLog);
}
var attributesString = "";
for (ii = 0; (ii < geocache.attributes_array.length); ii++) {
attributesString += getAttributeXML(geocache.attributes_array[ii]);
}
let gsakWptExtensions = '';
if (GM_getValue('gsakwptextensions', false)) {
gsakWptExtensions = gsakWptExtensionsTemplate
.replace('##LATORIGINAL##', geocache.coordinatesisedit ? geocache.latitude_original : '')
.replace('##LONORIGINAL##', geocache.coordinatesisedit ? geocache.longitude_original : '')
.replace('##CACHENOTE##', geocache.cache_note);
}
var geocacheMapping = [
['LAT', geocache.latitude],
['LON', geocache.longitude],
['TIME', xsdDateTime(geocache.dateHidden)],
['GCID', geocache.gcid],
['CACHEID', geocache.cacheid],
['GUID', geocache.guid],
['AVAILABLE', geocache.available],
['ARCHIVED', geocache.archived],
['CACHENAME', escapeHTML(geocache.cacheName)],
['CACHESYM', geocache.cacheSym],
['PLACEDBY', geocache.cachePlacedBy ? escapeHTML(geocache.cachePlacedBy) : escapeHTML(geocache.cacheOwner)],
['OWNER', escapeHTML(geocache.cacheOwner)],
['STATE', escapeHTML(geocache.state)],
['COUNTRY', escapeHTML(geocache.country)],
['TYPE', geocache.cacheType],
['CONTAINER', geocache.cacheSize],
['ATTRIBUTES', attributesString],
['DIFFICULTY', geocache.difficulty],
['TERRAIN', geocache.terrain],
['SUMMARY', escapeHTML(stripASCIIcodes(geocache.shortDescription))],
['DESCRIPTION', escapeHTML(stripASCIIcodes(geocache.longDescription))],
['HINT', escapeHTML(geocache.hint)],
['LOGS', logsStringArray.join("\n")],
['GSAKWPTEXTENSIONS', gsakWptExtensions]
];
// add favorite score in front of cache name (visible only in Geocaching menu of GPSr)
if (GM_getValue('gpxprefixfavscore', false)) {
// e.g. favscore = (<1%) --> remove "(", ")" and escape "<" to "&lt;"
var favScore = geocache.favScore ? geocache.favScore.replace(/[()]/g, '').replace(/</g, '&lt;') : '';
geocacheMapping.push(['FAVSCORE', favScore]);
geocacheTemplate = geocacheTemplate.replace('<groundspeak:name>##CACHENAME##</groundspeak:name>', '<groundspeak:name>##FAVSCORE####CACHENAME##<\/groundspeak:name>');
}
// bounds for caches
minLat = (!minLat) ? geocache.latitude : Math.min(geocache.latitude, minLat);
maxLat = (!maxLat) ? geocache.latitude : Math.max(geocache.latitude, maxLat);
minLon = (!minLon) ? geocache.longitude : Math.min(geocache.longitude, minLon);
maxLon = (!maxLon) ? geocache.longitude : Math.max(geocache.longitude, maxLon);
var cacheWaypoint = geocacheTemplate;
for (ii = 0; ii < geocacheMapping.length; ii++) {
cacheWaypoint = cacheWaypoint.replaceAll('##' + geocacheMapping[ii][0] + '##', geocacheMapping[ii][1]);
}
gcStrArray.push(cacheWaypoint);
if (GM_getValue('gpxwpts', true)) {
for (iii = 0; iii < geocache.additionalWaypoints.length; iii++) {
if (geocache.additionalWaypoints[iii].coordinates != "???") {
let wpt = getWaypointsGPXFromGeocache(geocache.additionalWaypoints[iii], geocache);
if (wpt !== '') {
if (GM_getValue('gpxwptasgeocache', false)) {
for (ii = 0; ii < geocacheMapping.length; ii++) {
wpt = wpt.replaceAll('##' + geocacheMapping[ii][0] + '##', geocacheMapping[ii][1]);
}
}
wptStrArray.push(wpt);
}
}
}
}
}
} // iteration end
// info of skipped PM only caches for BM (if any)
if (pmo.length > 0) {
pmo = $.gctour.lang('container.tourHeader.upload.tourUploadPMO') + ':<br>' + pmo;
pmo = '<small><i>' + pmo + '</i></small>';
showNotification({text: pmo, style: "yellow", timeout: 20000});
}
// info of retracted caches (if any)
if (retracted.length > 0) {
retracted = $.gctour.lang('container.tourHeader.upload.tourUploadRetracted') + ':<br>' + retracted;
retracted = '<small><i>' + retracted + '</i></small>';
showNotification({text: retracted, style: "yellow", timeout: 20000});
}
// bounds for cache waypoints + custom waypoints
var lat, lon;
for (i = 0; i < wptStrArray.length; ++i) {
lat = wptStrArray[i].split('lat="')[1].split('"')[0];
lon = wptStrArray[i].split('lon="')[1].split('"')[0];
minLat = (!minLat) ? lat : String(Math.min(lat, minLat));
maxLat = (!maxLat) ? lat : String(Math.max(lat, maxLat));
minLon = (!minLon) ? lon : String(Math.min(lon, minLon));
maxLon = (!maxLon) ? lon : String(Math.max(lon, maxLon));
}
var str = gpxHeader
.replaceAll('##GEOCACHES##', gcStrArray.join("\n"))
.replaceAll('##WAYPOINTS##', wptStrArray.join("\n"))
.replaceAll('##MINLAT##', minLat)
.replaceAll('##MINLON##', minLon)
.replaceAll('##MAXLAT##', maxLat)
.replaceAll('##MAXLON##', maxLon);
return str;
} catch (e) {
throw 'fn getGPX - ' + e;
}
}
async function downloadGPXFunction() {
try {
if (isEmptyTour(CURRENT_TOUR) || !isLoggedIn()) {
return;
}
var tourName,
currentDate,
currentDateString,
gpxString,
filename;
// add progressbar while loading
GM_setValue("stopTask", false);
addProgressbar({
closeCallback: function() {
return function() {
if (confirm($.gctour.lang('general.cancel') + "?") == true) {
window.location.reload();
}
};
}
});
tourName = CURRENT_TOUR.name.replace(/\s+/g, "_").replace(/[^A-Za-z0-9_ÄäÖöÜüß-]*/g, "");
currentDate = new Date();
currentDateString = currentDate.getFullYear() + "-" + (currentDate.getMonth() + 1) + "-" + currentDate.getDate() + "_" + currentDate.getHours() + "-" + currentDate.getMinutes() + "-" + currentDate.getSeconds();
filename = 'GCTour.' + tourName + '.' + currentDateString + '.gpx';
gpxString = await getGPX();
// if the cancel button is pressed the gpxString just contains canceled
if (gpxString == "canceled") {
closeOverlay();
return;
}
// open save file dialog
var file = new File([gpxString], filename, {
type: "application/gpx;charset=utf-8"
});
saveAs(file);
// all done - remove the overlay
closeOverlay();
} catch (e) {
addErrorDialog({
caption: "downloadGPXFunction error",
_exception: e
});
}
}
/* setting utilities */
function setLanguage(l) {
return function() {
GM_setValue('language', l);
window.location.reload();
};
}
function toggleBoolValue(valueName, defaultValue) {
return function() {
GM_setValue(valueName, !GM_getValue(valueName, defaultValue));
};
}
function setPrintFontSize(fontSize) {
return function() {
GM_setValue('printFontSize', fontSize);
};
}
function setPrintMapType(mapType) {
return function() {
GM_setValue('printOutlineMapType', mapType);
};
}
function setPrintMapSize(mapSize) {
return function() {
GM_setValue('defaultMapSize', mapSize);
};
}
/* settings jqUI prototype: properties and methods */
function Settings_jqUI() {
// checkbox settings
this.settings_printview_cb = [
['settings.printview.printMinimal', 'printMinimal', false],
['settings.printview.decryptHints', 'decryptPrintHints', true],
['settings.printview.editDescription', 'printEditMode', true],
['settings.printview.showSpoiler', 'printSpoilerImages', true],
['settings.printview.additionalWaypoints', 'printAdditionalWaypoints', true],
['settings.printview.additionalWaypoints2', 'addCustomMarkerToCacheTable', true],
['settings.printview.loggedVisits', 'printLoggedVisits', false],
['settings.printview.pageBreak', 'printPageBreak', false],
['settings.printview.pageBreakAfterMap', 'printPageBreakAfterMap', true],
['settings.printview.frontPage', 'printFrontpage', true],
['settings.printview.outlineMap', 'printOutlineMap', true],
['settings.printview.outlineMapSingle', 'printOutlineMapSingle', false],
['settings.printview.showCacheDetails', 'showCacheDetails', true],
['settings.printview.printCacheTableD', 'printCacheTableD', true],
['settings.printview.printCacheTableT', 'printCacheTableT', true],
['settings.printview.printCacheTableS', 'printCacheTableS', true],
['settings.printview.printCacheTableL4L', 'printCacheTableL4L', true],
['settings.printview.printCacheTableFav', 'printCacheTableFav', true],
['settings.printview.printCacheTableFound', 'printCacheTableFound', true],
['settings.printview.printCacheTableNote', 'printCacheTableNote', true]
];
this.settings_map_cb = [
['settings.map.geocacheid', 'settings_map_geocacheid', true],
['settings.map.geocachename', 'settings_map_geocachename', true],
['settings.map.geocacheindex', 'settings_map_geocacheindex', true],
['settings.map.awpts', 'settings_map_awpts', true],
['settings.map.awpt_name', 'settings_map_awpt_name', true],
['settings.map.awpt_lookup', 'settings_map_awpt_lookup', true],
['settings.map.owpts', 'settings_map_owpts', true],
['settings.map.owpt_name', 'settings_map_owpt_name', true]
];
this.settings_gpx_cb = [
['settings.gpx.html', 'gpxhtml', true],
['settings.gpx.wpts', 'gpxwpts', true],
['settings.gpx.ignoreParkWpts', 'gpxignoreparkwpts', false],
['settings.gpx.ignoreEqualListingCoordsWpts', 'gpxignoreequallistingcoordswpts', false],
['settings.gpx.wptAsGeocache', 'gpxwptasgeocache', false],
['settings.gpx.attributesToLog', 'gpxattributestolog', false],
['settings.gpx.stripGC', 'gpxstripgc', false],
['settings.gpx.prefixFavScore', 'gpxprefixfavscore', false],
['settings.gpx.gsakWptExtensions', 'gsakwptextensions', false]
];
// menu structure
this.tabs =
'<div id="tabs">' +
' <ul>' +
' <li><a href="#tabs-1">' + $.gctour.lang('settings.language.header') + '</a></li>' +
' <li><a href="#tabs-2">' + $.gctour.lang('settings.printview.header') + '</a></li>' +
' <li><a href="#tabs-3">' + $.gctour.lang('settings.map.header') + '</a></li>' +
' <li><a href="#tabs-4">' + $.gctour.lang('settings.gpx.header') + '</a></li>' +
' <li><a href="#tabs-5">' + $.gctour.lang('settings.theme.header') + '</a></li>' +
' <li><a href="#tabs-6">' + $.gctour.lang('settings.exportimport.header') + '</a></li>' +
' </ul>' +
' <div id="tabs-1">' + this.getLanguage() + '</div>' +
' <div id="tabs-2">' + this.getPrintview() + '</div>' +
' <div id="tabs-3">' + this.getMap() + '</div>' +
' <div id="tabs-4">' + this.getGpx() + '</div>' +
' <div id="tabs-5">' + this.getThemes() + '</div>' +
' <div id="tabs-6">' + this.getExportImport() + '</div>' +
'</div>';
}
Settings_jqUI.prototype.getLanguage = function() {
var language =
'<div id="lang">' +
' <label for="selLang" class="gctour-label ui-widget-header ui-corner-all" style="padding: .1em .5em;">' + $.gctour.lang('settings.language.select') + '</label>' +
' <select name="selLang" id="selLang" class="ui-widget ui-corner-all">';
$.each($.gctour.i18n, function(l, o) {
language += ' <option value="' + l + '">' + o.name + '</option>';
});
language +=
' </select>' +
'</div>';
return language;
};
Settings_jqUI.prototype.getPrintview = function() {
// first checkbox
var printview = this.cbLayout(this.settings_printview_cb[0][0]);
// log count
printview +=
'<div style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
' <b>' + $.gctour.lang('settings.printview.logCount') + '</b><br>' +
' <div id="settingsLogCount">' +
' <input type="radio" name="radio-1" id="radio-1" value="0">' +
' <label for="radio-1" class="ui-button ui-widget-header ui-corner-all" style="margin: 2px; padding: .1em .5em;">' + $.gctour.lang('settings.printview.logCounts')[0] + '</label><br>' +
' <input type="radio" name="radio-1" id="radio-2" value="3">' +
' <input type="text" style="width: 2em;" class="gctour-input-text" maxlength="3">' +
' <label for="radio-2" class="ui-button ui-widget-header ui-corner-all" style="margin: 2px; padding: .1em .5em;">' + $.gctour.lang('settings.printview.logCounts')[2] + '</label><br>' +
' </div>' +
'</div>';
// font size
printview +=
'<div id="selectmenu" style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
' <b>' + $.gctour.lang('settings.printview.fontSize') + '</b><br>' +
' <select name="settingsFontSize" id="settingsFontSize" class="ui-widget ui-corner-all">';
$.each($.gctour.lang('settings.printview.fontSizes'), function(i, val) {
printview += ' <option value="' + val + '">' + val + '</option>';
});
printview +=
' </select>' +
'</div>';
// remaining checkboxes
for (var i = 1; i < this.settings_printview_cb.length; i++) { // checkboxes
printview += this.cbLayout(this.settings_printview_cb[i][0]);
}
return printview;
};
Settings_jqUI.prototype.getMap = function() {
var map =
'<div style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
' <b>' + $.gctour.lang('settings.map.type') + '</b><br>' +
' <select name="settingsMapType" id="settingsMapType" class="ui-widget ui-corner-all">';
$.each($.gctour.lang('settings.map.types'), function(key,val) {
map += ' <option value="' + val + '">' + key + '</option>';
});
map +=
' </select>' +
'</div>' +
'<div style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
' <b>' + $.gctour.lang('settings.map.size') + '</b><br>' +
' <select name="settingsMapSize" id="settingsMapSize" class="ui-widget ui-corner-all">';
$.each($.gctour.lang('settings.map.sizes'), function(i,val) {
map += ' <option value="' + val + '">' + val + '</option>';
});
map +=
' </select>' +
'</div>';
for (var i = 0; i < this.settings_map_cb.length; i++) { // checkboxes
map += this.cbLayout(this.settings_map_cb[i][0]);
}
return map;
};
Settings_jqUI.prototype.getGpx = function() {
var gpx = '';
for (var i = 0; i < this.settings_gpx_cb.length - 3; i++) { // checkboxes
gpx += this.cbLayout(this.settings_gpx_cb[i][0]);
}
gpx +=
'<div id="maxLogCount" style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
' <b>' + $.gctour.lang('settings.gpx.maxLogCount') + '</b><br>' +
' <input type="text" style="width: 2em;" class="gctour-input-text" maxlength="3">' +
'</div>';
for (var i = this.settings_gpx_cb.length - 3; i < this.settings_gpx_cb.length; i++) { // checkboxes
gpx += this.cbLayout(this.settings_gpx_cb[i][0]);
}
return gpx;
};
Settings_jqUI.prototype.getThemes = function() {
var themes =
'<div id="ThemeSwitcher">' +
' <label for="selTheme" class="gctour-label ui-widget-header ui-corner-all" style="padding: .1em .5em;">' + $.gctour.lang('settings.theme.select') + '</label>' +
' <select id="selTheme" name="selTheme" class="ui-widget ui-corner-all">';
var themeArray = ["black-tie", "blitzer", "cupertino", "dark-hive", "dot-luv", "eggplant",
"excite-bike", "flick", "hot-sneaks", "humanity", "le-frog", "mint-choc", "overcast",
"pepper-grinder", "redmond", "smoothness", "south-street", "start", "sunny", "swanky-purse",
"trontastic", "ui-darkness", "ui-lightness", "vader"];
$.each(themeArray, function(i, theme) {
themes += ' <option value="' + theme + '">' + theme + '</option>';
});
themes +=
' </select>' +
'</div>';
return themes;
};
Settings_jqUI.prototype.getExportImport = function() {
var exp_imp =
'<div style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
'<b>' + $.gctour.lang('settings.exportimport.export') + '</b><br>' +
'<button id="ExportButton">Export</button>' +
'</div>' +
'<div style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
'<b>' + $.gctour.lang('settings.exportimport.import') + '</b><br>' +
'<input id="ImportButton" type="file" accept=".json" style="display:none;">' +
'<button onclick="document.getElementById(\'ImportButton\').click();">Import</button>' +
'</div>';
return exp_imp;
};
Settings_jqUI.prototype.show = function() {
// jQuery UI new settings menu
$(this.tabs).dialog($.gctour.dialog.info(), {
title : $.gctour.lang('settings.caption'),
width : '75%',
maxHeight : $(window).height() - 80,
resizable: false,
classes: { 'ui-dialog': 'gct_dialog' },
buttons : [{
text : $.gctour.lang('general.close'),
icons : {
primary : "ui-icon-closethick"
},
click : function() {
$(this).dialog("close");
}
}
],
beforeClose: function() {
// remove dialog leftovers
$("div.gct_dialog").remove();
}
});
// set position
$("div#tabs").parent().css({'position': 'absolute', 'top': '10px'});
// tabs menu
$("div#tabs").tabs({
heightStyle: "content"
});
// pre-selections
this.getCurrentSettings();
// import/export settings
$("button").button();
$('#ImportButton').on("change", importGCTourSettings);
$('#ExportButton').on("click", exportGCTourSettings);
reduceCacheTableSettings();
};
// pre-selection of current settings
Settings_jqUI.prototype.getCurrentSettings = function() {
var thiz = this;
// language
$("select#selLang").on('change', function() {
GM_setValue('language', $(this).val());
window.location.reload();
}).children("[value=" + GM_getValue('language', $.gctour.defaultLang) + "]").prop("selected", true);
// printview
var $radio = $('div#settingsLogCount input[type="radio"]'),
$text = $('div#settingsLogCount input[type="text"]'),
nLogs = GM_getValue('maxPrintLogs', 3);
if (nLogs === 0) { // no logs
$radio.eq(0).prop("checked", true);
} else { // fixed number of logs
$radio.eq(1).prop("checked", true);
$text.prop("value", nLogs);
}
$radio.eq(0).on('click', function() {
GM_setValue('maxPrintLogs', 0);
});
$radio.eq(1).on('click', function() {
GM_setValue('maxPrintLogs', $text.prop("value"));
});
$text.on('click', function() {
$radio.eq(1).prop("checked", true);
});
$text.on('keyup', function() {
thiz.checkInput($(this), 'maxPrintLogs');
});
$("select#settingsFontSize").on('change', function () {
GM_setValue('printFontSize', $(this).val());
}).children("[value=" + GM_getValue('printFontSize', 'x-small') + "]").prop("selected", true);
// maps
$("select#settingsMapType").on('change', function () {
GM_setValue('printOutlineMapType', $(this).val());
}).children("[value=" + GM_getValue('printOutlineMapType', 'roadmap') + "]").prop("selected", true);
$("select#settingsMapSize").on('change', function () {
GM_setValue('defaultMapSize', $(this).val());
}).children("[value=" + GM_getValue('defaultMapSize', 'large') + "]").prop("selected", true);
// gpx
var $text1 = $('div#maxLogCount input[type="text"]');
$text1.prop("value", GM_getValue('maxGPXLogs', 10));
$text1.on('keyup', function() {
thiz.checkInput($(this), 'maxGPXLogs');
});
// themes
$("select#selTheme").on('change', function () {
addJqUiTheme($(this).val());
GM_setValue('theme', $(this).val());
}).children("[value=" + GM_getValue('theme', 'smoothness') + "]").prop("selected", true);
// checkboxes
var settings_array = this.settings_printview_cb.concat(this.settings_map_cb).concat(this.settings_gpx_cb);
thiz = this;
$("input.gct-ui-checkbox").each(function(i) {
var item = settings_array[i];
thiz.createCheckBox($(this), item[0], item[1], item[2]);
// pre-selection
if (GM_getValue(item[1], item[2])) { // is checked
$(this).prop('checked', true);
}
});
};
// create checkboxes
Settings_jqUI.prototype.createCheckBox = function(elem, id, gmValue, dValue) {
elem.attr({
"id": id
})
.prop({
"type": "checkbox",
"checked": false
})
.css({
"float": "right",
"width": "1em",
"height": "1em"
})
.on('click', function() {
var isChecked = GM_getValue(gmValue, dValue);
GM_setValue(gmValue, !isChecked); // toggle value
});
};
// layout template for checkboxes
Settings_jqUI.prototype.cbLayout = function(setting) {
return '<div style="border-bottom:1px solid;padding-bottom:10px;padding-top:10px">' +
' <input class="gct-ui-checkbox">' +
' <b>' + $.gctour.lang(setting) + '</b><br>' +
$.gctour.lang(setting + "Desc") +
'</div>'
};
// check for int values in input fields
Settings_jqUI.prototype.checkInput = function(elem, ident) {
var value = elem.prop("value"),
intOnly = new RegExp(/^\d+$/);
if (intOnly.test(value)) {
elem.css("backgroundColor", '');
GM_setValue(ident, value);
} else {
elem.css("backgroundColor", '#ff7f7f');
}
};
// adding a jQuery UI theme to the end of document's head section
function addJqUiTheme(theme) {
var thmURL = "https://ajax.googleapis.com/ajax/libs/jqueryui/1.14.0/themes/" + theme + "/jquery-ui.css",
thmLink = $("<link>", {
id : "gct-ui-theme-link",
href : thmURL,
rel : "stylesheet",
type : "text/css"
});
$("head link#gct-ui-theme-link").remove(); // remove other theme(s) first
$("head").append(thmLink);
}
// export GCTour settings (including all tours)
function exportGCTourSettings() {
let settings = {};
for (let val of GM_listValues()) {
settings[val] = GM_getValue(val);
}
let file = new File([JSON.stringify(settings)], 'GCTour_Settings.json', {
type: "text/plain;charset=utf-8"
});
saveAs(file);
}
// import GCTour settings (including all tours)
function importGCTourSettings(evt) {
// check for the various File API support
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
} else {
alert('Sorry, the File APIs are not fully supported in your browser. Please update your browser.');
return;
}
let f = evt.target.files[0],
reader = new FileReader();
// closure to capture the file information
reader.onload = (function() {
return function(e) {
// here comes the file content!
let content = e.target.result;
try {
let parsed = JSON.parse(content);
$.each(parsed, function(key, value) {
if (parsed[key]) {
GM_setValue(key, value);
}
});
log_table(parsed);
window.location.reload();
} catch (e) {
alert('File content not valid:\n' + e);
}
};
})(f);
reader.readAsText(f, "UTF-8");
}
function reduceCacheTableSettings() {
let $section_div = $("input[id='settings.printview.printCacheTableD']").parent();
$section_div.append('<br>');
const cols = ["D","T","S","L4L","Fav","Found","Note"];
let $cb_div, text, $label, $input;
for (let i = 0; i < cols.length; ++i) {
$cb_div = $('input[id="settings.printview.printCacheTable' + cols[i] + '"]').parent();
// text
if (i > 4) text = $.gctour.lang('printview.' + cols[i].toLowerCase());
else text = cols[i];
// label and checkbox
$label = $("<label>").text(text).addClass("ui-button ui-widget-header ui-corner-all").css({ "padding": ".1em .5em" });
$input = $('input[id="settings.printview.printCacheTable' + cols[i] + '"]').css({ 'float': '', 'margin-left': '4px', 'margin-right': '0px', 'vertical-align': 'middle' });
// move checkbox into label and then to section div
$input.appendTo($label);
$section_div.append($label).append(" ");
// remove now superfluous div
if (i > 0) $cb_div.remove();
}
}
/* autoTour */
// autoTour gui
async function updateAutoTourMap(lat, lon) {
// make the container visible
$('#autoTourContainer').show();
// this is necessary if Leaflet map was hidden first and got visible later
LM._lm.invalidateSize();
var radiusOrg = $("input#markerRadius").val().trim();
if (isNaN(radiusOrg) || radiusOrg == "") { // please break if radius is no number
return;
}
var kmMiles = $("select#markerRadiusUnit").prop("selectedIndex");
// 0: km, 1: miles
var radiusMiles = parseFloat(radiusOrg) * ((kmMiles == 1) ? 1 : 0.621371);
if (radiusMiles == "") {
return;
}
// add circle
let radiusMeter = parseFloat(radiusOrg) / ((kmMiles == 0) ? 1 : 0.621371)*1000;
let circle = {};
circle.center = [lat,lon];
circle.radius = radiusMeter;
LM.removeLayer();
LM.addCircle(circle);
var url = GS_HOST + "seek/nearest.aspx?lat=" + lat + "&lng=" + lon + "&dist=" + radiusMiles;
$('b#markerCoordsPreview').empty().append(
$("<a>", {
href : url,
title : url,
text : new LatLon(lat, lon).toString()
})
.on('click', function() {
window.open(this.href);
return false;
}));
$('b#markerRadiusPreview').html(parseFloat(radiusOrg) + " " + ((kmMiles == 1) ? "mi" : "km"));
$("b#markerCoordsPreview, b#markerRadiusPreview")
.css("background-color", "#FFE000")
.animate({
"background-color" : "transparent"
}, 2000);
// get how many caches are in this area
$('b#markerCountPreview, b#markerDurationMin, b#markerDurationSec').html("<img src='" + $.gctour.img.loader3 + "'>");
var loadingTime1 = new Date();
var response = await promiseRequest('GET', url);
var dummyDiv = $(response.responseText),
color,
pagesSpan = $("td.PageBuilderWidget", dummyDiv).first();
if (pagesSpan.length > 0) {
var cacheCount = $("b", pagesSpan).first().text(),
nCalls = Math.ceil(cacheCount/200); // max. 200 caches per call
// time for single search call
var miliseconds = new Date() - loadingTime1;
// projected time for all search calls
var seconds = Math.ceil((miliseconds * parseFloat(nCalls)) / 1000);
// additional time for search page calls (e.g. waiting time)
seconds = seconds + parseFloat(nCalls) * 2;
var secondsMod = seconds % 60;
var minutes = (seconds - secondsMod) / 60;
$("b#markerCountPreview").html(cacheCount);
$("b#markerDurationMin").html(minutes);
$("b#markerDurationSec").html(secondsMod);
color = (cacheCount<1001) ? "#FFE000" : "#FF0000";
} else {
$("b#markerCountPreview, b#markerDurationMin, b#markerDurationSec").html("0");
color = "#FF0000";
}
$("b#markerCountPreview, b#markerDurationMin, b#markerDurationSec").css("background-color", color);
if (cacheCount<1001) {
$("b#markerCountPreview, b#markerDurationMin, b#markerDurationSec")
.animate({
"background-color" : "transparent"
}, 2000);
} else {
$("b#markerDurationMin, b#markerDurationSec")
.animate({
"background-color" : "transparent"
}, 2000);
}
// last, save the values
$('input#coordsDivLat').val(lat);
$('input#coordsDivLon').val(lon);
$('input#coordsDivRadius').val(radiusMiles);
}
async function updateAutoTourMapWebAPI(lat, lon) {
// make the container visible
$('#autoTourContainer').show();
// no duration estimate required anymore
$("b#markerDurationMin").parent().remove();
// this is necessary if Leaflet map was hidden first and got visible later
LM._lm.invalidateSize();
var radiusOrg = $("input#markerRadius").val().trim();
if (isNaN(radiusOrg) || radiusOrg == "") { // break if radius is no number
return;
}
var kmMiles = $("select#markerRadiusUnit").prop("selectedIndex");
// 0: km, 1: miles
var radiusMiles = parseFloat(radiusOrg) * ((kmMiles == 1) ? 1 : 0.621371);
if (radiusMiles == "") {
return;
}
// add circle
let radiusMeter = parseFloat(radiusOrg) / ((kmMiles == 0) ? 1 : 0.621371)*1000;
let circle = {};
circle.center = [lat,lon];
circle.radius = radiusMeter;
LM.removeLayer();
LM.addCircle(circle);
// new search link
var url = GS_HOST + "play/results/?st=" + Geo.toLat(lat) + "+" + Geo.toLon(lon) + "&ot=query&r=" + ((kmMiles == 1) ? radiusMiles : radiusMeter / 1000) + "&asc=true&sort=distance";
$('b#markerCoordsPreview').empty().append(
$("<a>", {
href : url,
title : url,
text : new LatLon(lat, lon).toString()
})
.on('click', function() {
window.open(this.href);
return false;
}));
$('b#markerRadiusPreview').html(radiusOrg + " " + ((kmMiles == 1) ? "mi" : "km"));
$("b#markerCoordsPreview, b#markerRadiusPreview")
.css("background-color", "#FFE000")
.animate({
"background-color" : "transparent"
}, 2000);
// get how many caches are in this area
$('b#markerCountPreview')
.css("background-color", "transparent")
.html("<img src='" + $.gctour.img.loader3 + "'>");
url = GS_HOST + 'api/proxy/web/search/v2?origin='+lat+','+lon+'&rad='+radiusMeter+'&take=0&sort=distance&asc=true&skip=0';
var response = await promiseRequest('GET', url);
var cacheCount = JSON.parse(response.responseText).total;
if (cacheCount > 0) {
$("b#markerCountPreview").html(cacheCount);
} else {
$("b#markerCountPreview").html("0");
}
if (cacheCount<=AUTOTOUR_MAX_CACHES) {
$("b#markerCountPreview")
.css("background-color", "#FFE000")
.animate({
"background-color" : "transparent"
}, 2000);
} else {
$("b#markerCountPreview").css("background-color", "#FF0000");
}
// last, save the values
$('input#coordsDivLat').val(lat);
$('input#coordsDivLon').val(lon);
$('input#coordsDivRadius').val(radiusMiles);
}
function startAutoTour() {
var typeFilter = {},
sizeFilter = {},
dFilter = {},
tFilter = {},
specialFilter = {},
ele = $("#autoTourContainer"),
lat,
lon,
radius,
url;
ele.find("input[name='type']").each(function() {
typeFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='size']").each(function() {
sizeFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='Difficulty']").each(function() {
dFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='Terrain']").each(function() {
tFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='special']").each(function() {
specialFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("div[id='special_pm']").children().each(function() {
if ($(this).is(':checked')) specialFilter['pm'] = $(this).val();
});
specialFilter['minFavorites'] = ele.find("input[id='special_favorites']").val();
lat = ele.find("input#coordsDivLat").val();
lon = ele.find("input#coordsDivLon").val();
radius = ele.find("input#coordsDivRadius").val();
url = GS_HOST + "seek/nearest.aspx?lat=" + lat + "&lon=" + lon + "&dist=" + radius;
if (specialFilter["I haven't found "]) {
url += "&f=1";
}
specialFilter["minFavorites"] = specialFilter["minFavorites"] || 0;
GM_setValue('tq_url', url);
GM_setValue('tq_typeFilter', JSON.stringify(typeFilter));
GM_setValue('tq_sizeFilter', JSON.stringify(sizeFilter));
GM_setValue('tq_dFilter', JSON.stringify(dFilter));
GM_setValue('tq_tFilter', JSON.stringify(tFilter));
GM_setValue('tq_specialFilter', JSON.stringify(specialFilter));
GM_setValue('tq_StartUrl', document.location.href);
debug("fn startAutoTour GM_setValue: " +
"\n\tq_url:" + url +
"\n\tq_typeFilter:" + JSON.stringify(typeFilter) +
"\n\tq_sizeFilter:" + JSON.stringify(sizeFilter) +
"\n\tq_dFilter:" + JSON.stringify(dFilter) +
"\n\tq_tFilter:" + JSON.stringify(tFilter) +
"\n\tq_specialFilter:" + JSON.stringify(specialFilter) +
"\n\tq_StartUrl:" + document.location.href);
document.location.href = "https://www.geocaching.com/seek/nearest.aspx";
}
async function startAutoTourWebAPI() {
try {
var typeFilter = {},
sizeFilter = {},
dFilter = {},
tFilter = {},
specialFilter = {},
ele = $("#autoTourContainer"),
nHits = Number(ele.find("#markerCountPreview").text());
if (nHits>AUTOTOUR_MAX_CACHES) {
showNotification({
text: $.gctour.lang('autoTour.maxCaches').replace('###NHITS###',nHits),
style: "yellow"
});
nHits = Math.min(nHits,AUTOTOUR_MAX_CACHES);
}
// get current filter settings
ele.find("input[name='type']").each(function() {
typeFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='size']").each(function() {
sizeFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='Difficulty']").each(function() {
dFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='Terrain']").each(function() {
tFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("input[name='special']").each(function() {
specialFilter[$(this).val()] = $(this).is(':checked');
});
ele.find("div[id='special_pm']").children().each(function() {
if ($(this).is(':checked')) specialFilter['pm'] = $(this).val();
});
specialFilter['minFavorites'] = ele.find("input[id='special_favorites']").val();
// store current filter settings for re-use
GM_setValue('tq_typeFilter', JSON.stringify(typeFilter));
GM_setValue('tq_sizeFilter', JSON.stringify(sizeFilter));
GM_setValue('tq_dFilter', JSON.stringify(dFilter));
GM_setValue('tq_tFilter', JSON.stringify(tFilter));
GM_setValue('tq_specialFilter', JSON.stringify(specialFilter));
var lat = ele.find("input#coordsDivLat").val();
var lon = ele.find("input#coordsDivLon").val();
var radius = ele.find("input#coordsDivRadius").val(); // miles
radius = Math.round(radius*1.609344*1000); // m
addProgressbar({
caption : $.gctour.lang('autoTour.wait'),
closeCallback : function() {
return function() {
closeOverlayRemote(document)();
};
}
});
var origin = lat+','+lon;
var take = 1000;
var url = GS_HOST + 'api/proxy/web/search/v2?origin='+origin+'&rad='+radius+'&take='+take+'&sort=distance&asc=true&skip=';
var skip = 0;
// number of calls
var nCalls = Math.ceil(nHits/take);
// for progress bar update
PROGRESS_BAR.progress = 0;
PROGRESS_BAR.total = nCalls;
var promises = [];
var url_fetchHits;
for (let i=0; i<nCalls; i++) {
skip = i*take;
url_fetchHits = url + skip;
promises.push(promiseRequest('GET', url_fetchHits, undefined, undefined, true));
}
var res = await Promise.all(promises);
// return all results, either resolved or rejected (https://davidwalsh.name/promise-allsettled)
//var res = await Promise.allSettled(promises);
var caches = [], cache, addBool, ind;
for (let i=0; i<res.length; i++) {
var hits = JSON.parse(res[i].responseText).results;
// loop through all hits
$.each(hits, function(j, hit) {
// map numerical to textual size
ind = SIZES_ARRAY.findIndex(obj => obj.newSearchId==hit.containerType);
if (ind != -1) hit.containerType = SIZES_ARRAY[ind].sizeTypeId;
else hit.containerType = "other"; // if numerical size is unknown, set size to other
// autoTour magic starts here (filter)
// check whether the cache matches the given type, size and D/T values
addBool = typeFilter[hit.geocacheType] &&
sizeFilter[hit.containerType] &&
dFilter[hit.difficulty] &&
tFilter[hit.terrain];
if (specialFilter["I haven't found "]) {
addBool = addBool && !hit.userFound;
}
if (specialFilter['is Active']) {
addBool = addBool && (hit.cacheStatus === 0);
}
if (specialFilter['pm'] == "premium") { // PM only
addBool = addBool && hit.premiumOnly;
} else {
if (specialFilter['pm'] == "basic") { // basic
addBool = addBool && !hit.premiumOnly;
}
}
addBool = addBool &&
(parseInt(((specialFilter['minFavorites']) ? specialFilter['minFavorites'] : 0), 10) <= parseInt(hit.favoritePoints, 10)); // minimal Favorites
// if all parameters match - add the cache
if (addBool) {
cache = {};
cache.name = hit.name;
cache.id = hit.code;
cache.image = GS_HOST + GS_WPT_IMAGE_PATH + hit.geocacheType + '.gif';
caches.push(cache);
}
/*debug(hit.code + " " + hit.name + " filter:" +
"\n\ttype:" + hit.geocacheType + " = " + typeFilter[hit.geocacheType] +
"\n\tsize:" + hit.containerType + " = " + sizeFilter[hit.containerType] +
"\n\tdifficulty:" + hit.difficulty + " = " + dFilter[hit.difficulty] +
"\n\tterrain:" + hit.terrain + " = " + tFilter[hit.terrain] +
"\n\thaven't found: " + !hit.userFound +
"\n\tactive: " + (hit.cacheStatus===0 ? true : false) +
"\n\tpm only: " + hit.premiumOnly +
"\n\t ==> Add to tour: " + addBool);*/
}); // END for each cache
}
CURRENT_TOUR = {};
CURRENT_TOUR.id = getNewTourId();
CURRENT_TOUR.name = "autoTour " + CURRENT_TOUR.id;
CURRENT_TOUR.geocaches = caches;
TOURS.push(CURRENT_TOUR);
log("autoTour done - create new Tour: " + CURRENT_TOUR.id + " ; " + CURRENT_TOUR.name);
saveCurrentTour();
updateTour();
closeOverlay();
showNotification({
text: $.gctour.lang('notifications.addgeocache.success.caption').format(CURRENT_TOUR.name),
style: "green",
timeout: 3000
});
} catch(e) {
closeOverlayRemote(document)();
if (e.includes("429")) var exc = 'message from Groundspeak: "too many requests"<br><br>Please decrease the radius.';
addErrorDialog({
caption: "error in startAutoTourWebAPI",
_exception: exc
});
}
}
// fetch and loop through results on old search page and save autoTour
async function processAutoTour() {
// progress bar
addProgressbar({
caption: $.gctour.lang('autoTour.wait'),
closeCallback: function() {
return function() {
if ( confirm($.gctour.lang('general.cancel')) ) {
GM_deleteValue('tq_url');
closeOverlayRemote(document)();
document.location.href = GM_getValue('tq_StartUrl', GS_HOST);
}
};
}
});
// search url
var tq_url = GM_getValue('tq_url');
// filter
var tq_typeFilter = JSON.parse(GM_getValue('tq_typeFilter')),
tq_sizeFilter = JSON.parse(GM_getValue('tq_sizeFilter')),
tq_dFilter = JSON.parse(GM_getValue('tq_dFilter')),
tq_tFilter = JSON.parse(GM_getValue('tq_tFilter')),
tq_specialFilter = JSON.parse(GM_getValue('tq_specialFilter'));
// get pages
// - to start, get page 1 of old search page
// - fetch all available pages from there (2 to max. 10)
// - if more pages are available, fetch the 11th
// - fetch all available pages from there (12 to max. 20)
// - ... and so on
var pages;
// get very first page
var page1 = await promiseRequest('GET',tq_url);
pages = [page1];
// if search is empty then finish
var timeout = 3000;
if ($("td.PageBuilderWidget > span:first",page1.responseText).length <= 0) {
closeOverlay();
showNotification({
text: "No caches here :-(",
style: "yellow",
timeout: timeout
});
GM_deleteValue('tq_url');
setTimeout(function() {
document.location.href = GM_getValue('tq_StartUrl', GS_HOST);
},timeout);
return;
}
// set progress bar counter
var cacheCount = Number($("td.PageBuilderWidget b",page1.responseText).first().text());
var count = 0;
PROGRESS_BAR.progress = Math.min(cacheCount,++count*200)-1;
PROGRESS_BAR.total = cacheCount;
setProgress(PROGRESS_BAR.progress,PROGRESS_BAR.total,document);
// get all remaining pages
var next10Pages = false,
res = [];
do {
// fetch next (max.) 10 pages
[res,next10Pages] = await fetchNextPages(page1);
pages = pages.concat(res); // add pages
if (next10Pages) {
page1 = pages[pages.length-1];
}
// update progress bar counter
PROGRESS_BAR.progress = Math.min(cacheCount, ++count * 200) - 1;
setProgress(PROGRESS_BAR.progress,PROGRESS_BAR.total,document);
} while (next10Pages);
closeOverlay();
// loop through all found caches
var addBool,
tq_caches = [],
entries = [],
len = pages.length;
for (let i=0; i<len; i++) {
// get all caches from page
entries = getEntriesFromOldSearchpage(pages[i].responseText);
// log("entries: " + JSON.stringify(entries));
// loop through all caches from page
$.each(entries, function(i, entry) {
//debug("entry[" + i + "]: " + JSON.stringify(entry));
// autoTour magic starts here (filter)
// check whether the caches match against the given type, size and D/T values
addBool = tq_typeFilter[entry.type] &&
tq_sizeFilter[entry.size] &&
tq_dFilter[entry.difficulty] &&
tq_tFilter[entry.terrain];
if (tq_specialFilter['is Active']) {
log("Check if " + entry.name + " is active:\n" +
"available: " + entry.available);
addBool = addBool && (entry.available); // only add if active!
}
if (tq_specialFilter['pm'] == "premium") { // PM only
addBool = addBool && entry.pm_only;
} else {
if (tq_specialFilter['pm'] == "basic") { // basic
addBool = addBool && !entry.pm_only;
}
}
// autoTour parameter "haven't found" is not checked here because of URL parameter
addBool = addBool &&
(parseInt(((tq_specialFilter['minFavorites']) ? tq_specialFilter['minFavorites'] : 0), 10) <= parseInt(entry.favorites, 10)); // minimal Favorites
// if all parameters match - add the cache
if (addBool) {
// 8.gif --> https://www.geocaching.com/images/wpttypes/sm/8.gif
entry.image = GS_HOST + GS_WPT_IMAGE_PATH + entry.image;
let tmp = {};
tmp.id = entry.id; tmp.guid = entry.guid; tmp.name = entry.name; tmp.image = entry.image;
tq_caches.push(tmp);
}
debug(entry.id + " " + entry.name + " filter:" +
"\n\ttype:" + entry.type + " = " + tq_typeFilter[entry.type] +
"\n\tsize:" + entry.size + " = " + tq_sizeFilter[entry.size] +
"\n\tdifficulty:" + entry.difficulty + " = " + tq_dFilter[entry.difficulty + ""] +
"\n\tterrain:" + entry.terrain + " = " + tq_tFilter[entry.terrain + ""] +
"\n\tavailable:" + entry.available +
"\n\tpm only:" + entry.pm_only +
"\n\t ==> Add to tour: " + addBool);
}); // END for each cache
}
// save as new tour
CURRENT_TOUR = {};
CURRENT_TOUR.id = getNewTourId();
CURRENT_TOUR.name = "autoTour " + CURRENT_TOUR.id;
CURRENT_TOUR.geocaches = tq_caches;
TOURS.push(CURRENT_TOUR);
log("autoTour done - create new Tour: " + CURRENT_TOUR.id + " ; " + CURRENT_TOUR.name);
saveCurrentTour();
// clean up and return to start url
showNotification({
text: $.gctour.lang('notifications.addgeocache.success.caption').format(CURRENT_TOUR.name),
style: "green",
timeout: timeout
});
GM_deleteValue('tq_url');
setTimeout(function() {
document.location.href = GM_getValue('tq_StartUrl', GS_HOST);
},timeout);
// help function: fetch next (max.) 10 pages
async function fetchNextPages(startPage) {
startPage = startPage.responseText;
// get form element from startPage
var theForm = $("input[name='__VIEWSTATE']",startPage).parent().parent()[0];
// top page links
var $href = $('a[href*="ctl00$ContentBody$pgrTop$lbGoToPage_"]',startPage);
var first = $href.length>0 ? parseInt( $href[0].href.split("_")[3].split(",")[0] ) : 0;
var last = $href.length>0 ? parseInt( $href.last()[0].href.split("_")[3].split(",")[0] ) : -1;
var clone,
$form,
promises = [];
var headers = {'Content-type' : 'application/x-www-form-urlencoded'};
for (let i=first; i<=last; i++) {
clone = theForm.cloneNode(true); // deep copy is important (reference is not enough!)
clone[0].value = "ctl00$ContentBody$pgrTop$lbGoToPage_"+i;
$form = $(clone);
promises.push( promiseRequest('POST', $form.prop('action'), headers, $form.serialize()) );
}
var next10Pages = false;
// check for link to next 10 pages
if ($('a[href*="ctl00$ContentBody$pgrTop$ctl06"]',startPage).length>0) {
next10Pages = true;
clone = theForm.cloneNode(true); // deep copy is important (reference is not enough!)
clone[0].value = "ctl00$ContentBody$pgrTop$ctl06";
$form = $(clone);
promises.push( promiseRequest('POST', $form.prop('action'), headers, $form.serialize()) );
}
var cache_objects = await Promise.all(promises);
return [cache_objects,next10Pages];
}
}
async function getMarkerCoordsAndUpdateMap() {
var markerCoords = $("input#markerCoords").val();
var coords = await parseCoordinates(markerCoords, true);
if (coords) {
if (AUTOTOUR_WEBAPI) {
await updateAutoTourMapWebAPI(coords._lat, coords._lon);
} else {
await updateAutoTourMap(coords._lat, coords._lon);
}
} else { // even a geocoding service cannot determine the coordinates
alert("'" + markerCoords + "' cannot be recognized as address.");
}
}
function addCheckUncheckAllLinks($div) {
var checkboxes = $div.find('input[type=checkbox]'),
$all_none = $('<span>')
.append(
$('<a style="cursor:pointer;">').html('<i>' + $.gctour.lang('settings.printview.logCounts')[1] + '</i>').on('click', function() {
checkboxes.prop('checked', true);
}))
.append(' / ')
.append(
$('<a style="cursor:pointer;">').html('<i>' + $.gctour.lang('settings.printview.logCounts')[0] + '</i>').on('click', function() {
checkboxes.prop('checked', false);
}))
.append($('<br>'));
$all_none.insertBefore($div.find('span:first'));
$div.find('a')
.css({
'color' : '#00447c',
'text-decoration' : 'underline'
})
.on("mouseenter", function () {
$(this).css("color", "#6c8e10");
})
.on("mouseleave", function () {
$(this).css("color", "#00447c");
});
}
function getSpecialFilter() {
var $div,
$checkbox,
$favorites,
attributs,
checkbox_specials = {
'I haven\'t found ' : $.gctour.lang('autoTour.filter.special.notfound'),
'is Active' : $.gctour.lang('autoTour.filter.special.isActive')
},
initial = '{"I haven\'t found ":false,"is Active":false,"pm":"all","minFavorites":"0"}',
tq_filter = JSON.parse(GM_getValue('tq_specialFilter', initial));
$div = $('<div>')
.html("<b>" + $.gctour.lang('autoTour.filter.special.caption') + "</b><br/>")
.css({
"padding-right" : "10px"
});
// PM
let $radio = $(
'<div id="special_pm">' +
' <input type="radio" id="all" name="membership" value="all">' +
' <label for="all">' + $.gctour.lang('autoTour.filter.special.pm.all')+'</label>' +
' <input type="radio" id="basic" name="membership" value="basic">' +
' <label for="basic">' + $.gctour.lang('autoTour.filter.special.pm.basic')+'</label>' +
' <input type="radio" id="premium" name="membership" value="premium">' +
' <label for="premium">' + $.gctour.lang('autoTour.filter.special.pm.premium')+'</label>' +
'</div>'
);
// mapping of old filter ids to new ids
if (!tq_filter["pm"]) tq_filter["pm"] = "all"; // no filter for PM available, use default value
else {
let idMap = { "ignore": "all", "not": "basic", "only": "premium" };
for (let key in idMap) {
if (key == tq_filter["pm"]) {
tq_filter["pm"] = idMap[key];
break;
}
}
}
$('#' + tq_filter["pm"], $radio).prop("checked", true);
$div.append($radio, $('<br>'));
// not found, active
$.each(checkbox_specials, function(key, value) {
attributs = {
type : 'checkbox',
name : "special",
class : "gctour-input-checkbox",
id : "special" + key,
value : key,
checked : tq_filter[key] ? 'checked' : false
};
$checkbox = $('<span>')
.css({
"margin" : "2px",
"vertical-align" : "middle"
})
.append(
$('<input/>', attributs).css({
"margin" : "0 4px 0 0"
}),
$('<label>')
.attr({
"for" : "special" + key,
"class" : "gctour-label"
})
.text(value));
$div.append(
$checkbox,
$('<br>'));
});
// min favorites
$favorites = $('<input>', {
type : 'text',
id : 'special_favorites',
class : 'gctour-input-text',
value : tq_filter['minFavorites']
}).css({
'margin' : '4px 0 0 4px',
'width' : '40px'
});
$div.append(
$('<span>').text($.gctour.lang('autoTour.filter.special.minFavorites')),
$favorites,
$('<br>'));
return $div;
}
function getDtFilter(boxName) {
var $div,
$checkbox,
attributs,
tq_filter,
initial = '{"1":true,"2":true,"3":true,"4":true,"5":true,"1.5":true,"2.5":true,"3.5":true,"4.5":true}',
title;
if (boxName == 'Difficulty') {
tq_filter = JSON.parse(GM_getValue('tq_dFilter', initial));
title = $.gctour.lang('autoTour.filter.difficulty');
} else { // terrain
tq_filter = JSON.parse(GM_getValue('tq_tFilter', initial));
title = $.gctour.lang('autoTour.filter.terrain');
}
$div = $('<div>')
.html("<b>" + title + "</b><br/>");
for (var i = 1; i <= 5; i = i + 0.5) {
attributs = {
type : 'checkbox',
name : boxName,
class : "gctour-input-checkbox",
id : boxName + "" + i,
value : i,
checked : tq_filter["" + i] ? 'checked' : false
};
$checkbox = $('<span>')
.css({
"margin" : "2px",
"vertical-align" : "middle"
})
.append(
$('<input/>', attributs).css({
"margin" : "0 2px 0 0"
}),
$('<label>').attr({
"for" : boxName + "" + i,
"class" : "gctour-label"
})
.append(
$('<img>').attr("src", GS_HOST + "images/stars/stars" + ("" + i).replace(/\./g, "_") + ".gif").css({
"vertical-align" : "unset"
})));
$div.append(
$checkbox,
$('<br>'));
}
addCheckUncheckAllLinks($div);
return $div;
}
function getSizeFilter() {
var $div,
$checkbox,
attributs,
initial = '{"micro":true,"small":true,"regular":true,"large":true,"other":true,"not_chosen":true,"virtual":true}',
tq_filter = JSON.parse(GM_getValue('tq_sizeFilter', initial));
$div = $('<div>')
.html("<b>" + $.gctour.lang('autoTour.filter.size') + "</b><br/>");
$.each(SIZES_ARRAY, function(index, size) {
attributs = {
type : 'checkbox',
name : "size",
class : "gctour-input-checkbox",
id : "size" + size.sizeTypeId,
value : size.sizeTypeId,
checked : tq_filter[size.sizeTypeId] ? 'checked' : false
};
$checkbox = $('<span>')
.css({
"margin" : "2px",
"vertical-align" : "middle"
})
.append(
$('<input/>', attributs).css({
"margin" : "0 2px 0 0"
}),
$('<label>').attr({
"for" : "size" + size.sizeTypeId,
"class" : "gctour-label"
})
.append(
$('<img>').attr({
"src": GS_HOST + 'images/icons/container/' + size.sizeTypeId + '.gif',
"title": size.name
}).css({
"vertical-align" : "unset"
})));
$div.append(
$checkbox,
$('<br>'));
});
addCheckUncheckAllLinks($div);
return $div;
}
function getTypeFilter() {
var $div,
$checkbox,
attributs,
boo,
initial = '{"2":true,"3":true,"4":true,"5":true,"6":true,"8":true,"9":true,"11":true,"12":true,"13":true,"137":true,"453":true,"1304":true,"1858":true,"3653":true,"3773":true,"3774":true,"4738":true,"7005":true}',
tq_filter = JSON.parse(GM_getValue('tq_typeFilter', initial));
$div = $('<div>')
.html("<b>" + $.gctour.lang('autoTour.filter.type') + "</b><br/>")
.css({
"padding-left" : "10px"
});
$.each(WPT_ARRAY, function(index, wpt) {
attributs = {
type : 'checkbox',
name : "type",
class : "gctour-input-checkbox",
id : "type" + wpt.wptTypeId,
value : wpt.wptTypeId,
checked : tq_filter[wpt.wptTypeId] ? 'checked' : false
};
boo = ((index + 1) % 2 === 0); // letzter in seiner Spalte ?
$checkbox = $('<span>')
.css("padding-left", boo ? "10px" : "0px")
.append(
$('<input/>', attributs).css({
"margin" : "0 2px 0 0"
}),
$('<label>').attr({
"for" : "type" + wpt.wptTypeId,
"class" : "gctour-label"
})
.append(
$('<img>').attr({
"src": GS_HOST + GS_WPT_IMAGE_PATH + wpt.wptTypeId + '.gif',
"title": wpt.name
}).css({
"vertical-align" : "unset"
})));
$div.append(
$checkbox,
(boo ? $('<br>') : ""));
});
addCheckUncheckAllLinks($div);
return $div;
}
function getCoordinatesTab() {
var coordsDiv = $("<div>", {
id : "coordsDiv"
});
// km or miles
let km_selected = 'selected="selected"';
let miles_selected = '';
if (GS_USERINFO.unitSetName === "Imperial") {
km_selected = '';
miles_selected = 'selected="selected"';
}
var grid =
'<div style="display: grid;grid-template-columns: 13% auto auto;grid-template-rows: 18px auto auto;">' +
' <div><b>' + $.gctour.lang('autoTour.center') + ':</b></div>' +
' <div>' +
' <input type="text" id="markerCoords" class="gctour-input-text" style="width:200px;margin:0;" placeholder="Coordinates, address">' +
' <button id="autoTour_locateme" style="font-size: 12px; cursor: pointer; height: 18px;">' +
' <img id="locateImage" src="'+$.gctour.img.locateMe+'" style="height: 12px;">' +
' <span style="vertical-align:top;margin-left:3px;">'+$.gctour.lang('general.findMe')+'</span>' +
' </button>' +
' </div>' +
' <div style="justify-self: end;" id="divStartQuery">'+
' <button id="autoTour_refresh" style="display: none; font-size: 12px; cursor: pointer; height: 35px;">' +
' <img src="'+$.gctour.img.update+'" style="height: 12px;">' +
' <span style="vertical-align:top;margin-left:3px;">'+$.gctour.lang('autoTour.refresh')+'</span>' +
' </button>' +
' </div>' +
' <div style="grid-column: 1 / 4;margin-top: 7px;"></div>' + // empty row for some space
' <div><b>' + $.gctour.lang('autoTour.radius') + ':</b></div>' +
' <div>' +
' <input type="text" id="markerRadius" class="gctour-input-text" maxlength="4" value="2" style="width:40px;margin-right:5px;margin-top:0;margin-bottom:0;">' +
' <select id="markerRadiusUnit">' +
' <option value="km" ' + km_selected + '>' + $.gctour.lang('units.km') + '</option>' +
' <option value="mi" ' + miles_selected + '>' + $.gctour.lang('units.mi') + '</option>' +
' </select>' +
' </div>' +
'</div>';
coordsDiv.append(grid);
// add autoTour start button
$('div#divStartQuery',coordsDiv).append(getAutoTourSubmit());
// toggle visibility of start and update buttons
$('input#markerCoords, input#markerRadius, select#markerRadiusUnit',coordsDiv).on('input', function() {
$('button#startQuery').css('display','none');
$('button#autoTour_refresh').css('display','block');
});
// add click events to buttons
$('button#autoTour_refresh',coordsDiv).on('click', function() {
getMarkerCoordsAndUpdateMap();
// toggle visibility of start and update buttons
$('button#startQuery').css('display','block');
$('button#autoTour_refresh').css('display','none');
});
$('button#autoTour_locateme',coordsDiv).on('click', function() {
if (navigator.geolocation) {
$('locateImage').attr("src", $.gctour.img.loader3);
navigator.geolocation.getCurrentPosition(
function(position) {
$('locateImage').attr("src", $.gctour.img.locateMe);
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
$("input#markerCoords").val(new LatLon(latitude, longitude).toString()).trigger('input');
},
function(error) {
$('locateImage').attr("src", $.gctour.img.locateMe);
log('Unable to get current location: ' + error.message);
},
{ timeout: 10000 }
);
} else {
alert("Sorry, your browser does not support geolocation.");
}
});
// add syntax check for radius input
$('input#markerRadius',coordsDiv).on('keydown keypress keyup paste input', function() {
this.value = this.value.replace(/,/g,".").replace(/[^0-9.]/g, '').replace(/(\..*)\./g, '$1');
});
return coordsDiv;
}
function getMapPreviewTab() {
// parent div
var previewDiv = createElement('div');
// hidden child items
var coordsInputLat = createElement('input', {
type : 'hidden',
id : "coordsDivLat"
});
previewDiv.appendChild(coordsInputLat);
var coordsInputLon = createElement('input', {
type : 'hidden',
id : "coordsDivLon"
});
previewDiv.appendChild(coordsInputLon);
var coordsInputRadius = createElement('input', {
type : 'hidden',
id : "coordsDivRadius"
});
previewDiv.appendChild(coordsInputRadius);
// center, radius, cache count and duration preview
var grid = createElement('div', {
style: "display: grid;grid-template-columns: auto auto auto auto;"
});
var center = createElement('div');
center.innerHTML = $.gctour.lang('marker.coord') + ": <b id='markerCoordsPreview'>???</b>";
grid.appendChild(center);
var radius = createElement('div');
radius.innerHTML = $.gctour.lang('autoTour.radius') + ": <b id='markerRadiusPreview'>???km</b>";
grid.appendChild(radius);
var cacheCount = createElement('div');
cacheCount.innerHTML = $.gctour.lang('autoTour.cacheCounts') + ": <b id='markerCountPreview'>???</b>";
grid.appendChild(cacheCount);
var duration = createElement('div');
duration.innerHTML = $.gctour.lang('autoTour.duration') + ": <b id='markerDurationMin'>???</b> min <b id='markerDurationSec'>???</b> s";
grid.appendChild(duration);
previewDiv.appendChild(grid);
// previewMap
var leafletMap = createElement('div', {
style: "margin-bottom: 10px;"
});
leafletMap.id = 'gct-leafletMap';
previewDiv.appendChild(leafletMap);
return previewDiv;
}
function getAutoTourSubmit() {
var $submit = $("<button>", {
id: "startQuery",
title: "start autoTour",
css: {
"margin": "auto",
"display": "block",
"cursor": "pointer"
},
html: "<img src ='" + $.gctour.img.startAutoTour + "'>"
})
.on('click', (AUTOTOUR_WEBAPI ? startAutoTourWebAPI : startAutoTour));
return $submit;
}
async function showAutoTourDialog(center, radius) {
var overLay = getOverlay({
caption : '<b>' + $.gctour.lang('autoTour.title') + '</b>',
minimized : true
});
$(overLay).append(
getCoordinatesTab(),
$("<div>", {
id : "autoTourContainer",
css : {
"display" : "none",
"clear" : "both",
"border-top" : "2px dashed #B2D4F3",
"margin-top" : 5
}
}).append(
getMapPreviewTab(),
$("<div>", {
css : {
"display": "grid",
"background": "lightgrey"
}
}).append(
$("<div>", {
css : {
"justify-self": "center"
}
}).html('<b style="font-size: 1.2em;">'+$.gctour.lang('autoTour.filterLabel')+'</b>')
),
$('<div>', {
css : {
"background-color": "lightyellow",
"display": "grid",
"grid-template-columns": "auto auto auto auto auto",
"grid-column-gap": "10px"
}
}).append(
getTypeFilter(),
getSizeFilter(),
getDtFilter('Difficulty'),
getDtFilter('Terrain'),
getSpecialFilter())
));
if (center && radius) {
$("input#markerCoords").val(new LatLon(center.lat, center.lng).toString());
if (GS_USERINFO.unitSetName === "Imperial") {
radius *= 0.621371; // miles
}
$("input#markerRadius").val(Math.round((Number(radius) + Number.EPSILON) * 100) / 100); // restrict to 2 decimals
} else {
$("input#markerRadius").val(2);
// on cache page get center point from cache coordinates, else from profile settings
var latlon;
if (!(latlon = $('span#uxLatLon').text()))
latlon = await getHomeCoords();
$('input#markerCoords').val(latlon);
}
// create map
let $map = $('div#gct-leafletMap');
let options = {};
options.id = 'gct-autoTour-map';
options.center = [0,0];
options.height = '350px';
options.anchor = $map;
LM = new LeafletMap(options);
getMarkerCoordsAndUpdateMap();
}
// waypoint projection, earth approx. as sphere (now working in northern and southern hemisphere)
function CalcPrjWP(lat, lon, dist, angle) {
var lat1 = parseFloat(lat), // degree
lon1 = parseFloat(lon), // degree
Dist = parseFloat(dist), // miles
Angle = parseFloat(angle), // degree
d,
R,
mile,
lat2,
lon2;
while (Angle > 360) {
Angle = Angle - 360;
}
while (Angle < 0) {
Angle = Angle + 360;
}
// calculations in rad
lat1 = lat1 / 180 * Math.PI;
lon1 = lon1 / 180 * Math.PI;
Angle = Angle / 180 * Math.PI;
R = 6371.0; // mean earth radius in km
mile = 1.609344;
d = Dist * mile / R; // distance in km
lat2 = Math.asin(Math.sin(lat1) * Math.cos(d) + Math.cos(lat1) * Math.sin(d) * Math.cos(-Angle));
if (Math.cos(lat1) == 0) {
lon2 = lon1;
} else {
lon2 = (lon1 - Math.asin(Math.sin(-Angle) * Math.sin(d) / Math.cos(lat1)) + Math.PI) % (2 * Math.PI) - Math.PI;
}
// results in deg
lat2 = lat2 / Math.PI * 180;
lon2 = lon2 / Math.PI * 180;
return [Math.round(lat2 * 100000) / 100000, Math.round(lon2 * 100000) / 100000];
}
// get home coordinates from GS Web API
async function getHomeCoords() {
try {
let url = GS_WEB_API + 'webuser';
let headers = { 'Accept': 'application/json' };
let response = await promiseRequest("GET", url, headers);
let obj = JSON.parse(response.responseText);
let latLon = new LatLon(obj.homeCoordinate.latitude, obj.homeCoordinate.longitude);
let homeCoords = latLon.toString("dm");
return homeCoords;
} catch(e) {
// fallback
log('getHomeCoords error: "' + e + '"\nusing default coordinates');
return "N 49°26.082 E 007°46.587";
}
}
// compare script version strings: if a > b return true else return false
function isNewVersion(a, b) {
if (a === b) {
return false;
}
var a_components = a.split(".");
var b_components = b.split(".");
var len = Math.min(a_components.length, b_components.length);
// loop while the components are equal
for (var i = 0; i < len; i++) {
// A bigger than B
if (parseInt(a_components[i]) > parseInt(b_components[i])) {
return true;
}
// B bigger than A
if (parseInt(a_components[i]) < parseInt(b_components[i])) {
return false;
}
}
// if one is a prefix of the other, the longer one is bigger
if (a_components.length > b_components.length) {
return true;
}
if (a_components.length < b_components.length) {
return false;
}
}
// Show changelog hint
function changelog() {
let div = '<div id="gct_changelog" style="width:100vw;background-color:#e0b70a;vertical-align:middle;text-align:center;display:table-cell;font-size:0.8rem;">'
+ $.gctour.lang('dlg.newVersion.changelog')
+ '<a href="https://gist.github.com/DieBatzen/5814dc7368c1034470c8/raw/gctour.zChangelog.txt" target="_blank">Changelog</a>'
+ '<img id="gct_close_changelog" title="' + $.gctour.lang('general.close') + '" style="cursor: pointer;float: right;margin-right: 10px;margin-top: 5px;" src="' + $.gctour.img.closebutton + '">'
+ '</div>';
$('body').prepend(div);
$('nav#gcNavigation').attr('style', 'position:static !important;font-size: 0.8rem;');
$('img#gct_close_changelog').on('click', function() {
$('div#gct_changelog').remove();
});
}
// installation counter and Changelog (idea from GC little helper II: https://github.com/2Abendsegler/GClh)
function instCount() {
let ver = GM_getValue("version","0");
if (isNewVersion(VERSION, ver)) { // script has been updated
GM_setValue("version",VERSION);
// show Changelog hint
setTimeout(function() { changelog(); }, 5000);
// counter
//$(document.body).append('<div id="gct_counter1"><img src="https://www.worldflagcounter.com/dVE" style="visibility:hidden;"></div>');
//setTimeout(function() { $("#gct_counter1").remove(); }, 3000);
$(document.body).append('<div id="gct_counter2"><img referrerpolicy="no-referrer" src="https://s01.flagcounter.com/count2/UOBh/bg_FFFFFF/txt_000000/border_CCCCCC/columns_2/maxflags_250/viewers_4.39/labels_1/pageviews_1/flags_0/percent_0/" alt="Flag Counter" style="visibility:hidden;"></div>');
setTimeout(function() { $("#gct_counter2").remove(); }, 3000);
}
}
// update notifier for Gist repository
async function update(force) {
var updateURL = GM_info.scriptMetaStr.split('updateURL')[1].split('// @')[0].trim();
var downloadURL = GM_info.scriptMetaStr.split('downloadURL')[1].split('// @')[0].trim();
var updateDate = new Date(GM_getValue('updateDate'));
if (!updateDate || updateDate == "Invalid Date") {
updateDate = new Date();
GM_setValue('updateDate', updateDate.toString());
}
instCount();
var currentDate = new Date();
// if the last update check is more than 86 400 000 msec (1 day) ago - check for updates
if ((currentDate.getTime() - updateDate.getTime() > 86400000) || (force === true)) {
// set the new updateDate
GM_setValue('updateDate', currentDate.toString());
var response = await promiseRequest('GET', updateURL);
var text = response.responseText;
var ver_gist = text.split("version")[1].split("//")[0].trim();
if (!isNewVersion(ver_gist,VERSION)) { // no update available
log("update check: version " + VERSION + " is up to date");
if (force === true) {
alert($.gctour.lang('update.upToDate'));
}
return;
}
var overlayBody = getOverlay({
caption : $.gctour.lang('dlg.newVersion.caption'),
minimized : true
});
var updateMapping = [
['VERSION_OLD', VERSION],
['VERSION_NEW', ver_gist]
];
var confirmString = $.gctour.lang('update.dialog');
var update_dom = fillTemplate(updateMapping, confirmString);
var footer = update_dom.getElementsByTagName('div')[update_dom.getElementsByTagName('div').length - 1];
// if install is pressed set the document.location to the update url
var install_button = document.createElement('input');
install_button.type = "button";
install_button.value = $.gctour.lang('general.install');
install_button.style.backgroundImage = "url(" + $.gctour.img.userscript + ")";
install_button.addEventListener('click', function() {
setTimeout(closeOverlay, 500);
document.location = downloadURL;
}, true);
var close_button = document.createElement('input');
close_button.type = "button";
close_button.value = $.gctour.lang('general.cancel');
close_button.style.backgroundImage = "url(" + $.gctour.img.closebutton + ")";
close_button.addEventListener('click', closeOverlay, false);
footer.appendChild(close_button);
footer.appendChild(install_button);
overlayBody.appendChild(update_dom);
}
}
/* helper */
// Funktion, die in einem String die Wildcard {0},{1},{2}... mit den Paramtern von String.format ersetzt!
String.prototype.format = function() {
var s = this;
for (var i = 0; i < arguments.length; i++) {
var reg = new RegExp("\\{" + i + "\\}", "gm");
s = s.replace(reg, arguments[i]);
}
return s;
}
// Convert HTML breaks to spaces
String.prototype.br2space = function() {
return this.replace(/<br\s*\/?>/mg, " ");
}
// Return a new string without leading and trailing whitespace
// Double spaces within the string are removed as well
String.prototype.trimAll = function() {
return this.replace(/^\s+|(\s+(?!\S))/mg, "");
}
// replace all occurrences of str1 by str2 inside a string; optionally ignore case
// (http://dumpsite.com/forum/index.php?topic=4.msg29#msg29)
String.prototype.replaceAll = function(str1, str2, ignoreCase) {
return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignoreCase?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
}
function gmNotice() {
let str_DE =
'<span>' +
'Ab Firefox Version 57, die Mitte November 2017 ansteht, wird GCTour mit Greasemonkey nicht mehr funktionieren. Den Grund dafür kann man hier nachlesen: ' +
'<a href="http://www.greasespot.net/2017/09/greasemonkey-4-announcement.html" target="_blank">Link.</a><br><br>' +
'Einfache Lösung: in Firefox eine andere Erweiterung für Userskripte verwenden, z.B. <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>. ' +
'Damit läuft GCTour weiterhin einwandfrei.<br>' +
'<ol><li>Zuerst alle GCTour-Einstellungen, inklusive aller Touren sichern (NEU): "Einstellungen - Export/Import - Export"' +
'<li>Greasemonkey deinstallieren oder deaktivieren ("about:addons")' +
'<li>Tampermonkey installieren: <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>' +
'<li>GCTour neu installieren: <a href="' + GCTOUR_HOST + 'files/gctour.user.js" target="_blank">GCTour</a>' +
'<li>Alle GCTour-Einstellungen, inklusive aller Touren wiederherstellen (NEU): "Einstellungen - Export/Import - Import"' +
'</ol>' +
'</span>';
let str_EN =
'<span>' +
'As of Firefox version 57 in mid-November 2017, GCTour will no longer work with Greasemonkey. The reason for this can be read here: ' +
'<a href="http://www.greasespot.net/2017/09/greasemonkey-4-announcement.html" target="_blank">Link.</a><br><br>' +
'Simple solution: in Firefox use another extension for user scripts, for example <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>. ' +
'Thus GCTour continues to run flawlessly.<br>' +
'<ol><li>First save all GCTour settings, including all tours (NEW): "Settings - Export/Import - Export".' +
'<li>Uninstall or deactivate Greasemonkey ("about:addons")' +
'<li>Install Tampermonkey: <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>' +
'<li>Reinstall GCTour: <a href="' + GCTOUR_HOST + 'files/gctour.user.js" target="_blank">GCTour</a>' +
'<li>Restore all GCTour settings, including all tours (NEW): "Settings - Export/Import - Import"' +
'</ol>' +
'</span>';
let str = str_DE + '<hr>' + str_EN;
$('<div>'+str+'</div>').dialog($.gctour.dialog.info(),{title: 'Hinweis / Notice',width: '75%',maxHeight: $(window).height() - 50});
$(".ui-button span").text($.gctour.lang('general.close'));
}
// try to get GS user info in that order:
// 1) if user info is embedded in page, no need to fetch:
// i) serverParameters["user:info"] (old pages)
// ii) _gcUser (new search map)
// iii) __NEXT_DATA__ (new tech stack)
// 2) fetch by webAPI (identical info as 1ii)
// 3) fetch from play/serverparameters/params (identical info as 1i)
// 4) fetch from play/dataparameters/datalayer and account/settings/preferences
// 5) if 1-4 all fail set default values
async function getGSUserInfo() {
log_timeStart('getGSUserInfo');
try { // 1i)
GS_USERINFO = unsafeWindow.serverParameters["user:info"];
} catch (e) { // 1ii)
try {
GS_USERINFO = unsafeWindow._gcUser;
GS_USERINFO.userType = GS_USERINFO.membershipLevel === 1 ? "Basic" : "Premium";
GS_USERINFO.isLoggedIn = true;
GS_USERINFO.unitSetName = GS_USERINFO.distanceUnit === 2 ? "Imperial" : "Metric";
} catch (e) { // 1iii)
try {
GS_USERINFO = {};
let tmp;
if (typeof unsafeWindow.__NEXT_DATA__.text === 'undefined') {
tmp = unsafeWindow.__NEXT_DATA__.props.pageProps.gcUser;
} else {
tmp = JSON.parse(unsafeWindow.__NEXT_DATA__.text).props.pageProps.gcUser;
}
GS_USERINFO.dateFormat = tmp.dateFormat;
GS_USERINFO.userType = tmp.membershipLevel === 1 ? "Basic" : "Premium";
GS_USERINFO.isLoggedIn = true;
GS_USERINFO.unitSetName = tmp.distanceUnit === 2 ? "Imperial" : "Metric";
} catch (e) { // 2)
log("fn getGSUserInfo - user info not embedded in page --> fetch from webAPI");
GS_USERINFO = {};
try {
let url = GS_WEB_API + 'webuser',
header = { 'Content-Type': 'application/json' };
let response = await promiseRequest("GET", url, header);
let obj = JSON.parse(response.responseText);
GS_USERINFO.dateFormat = obj.dateFormat;
GS_USERINFO.userType = obj.membershipLevel === 1 ? "Basic" : "Premium";
GS_USERINFO.isLoggedIn = true;
GS_USERINFO.unitSetName = obj.distanceUnit === 2 ? "Imperial" : "Metric";
} catch (e) { // 3)
log("fn getGSUserInfo - fetching from webAPI unsuccessful --> fetch from play/serverparameters/params");
try {
let response = await promiseRequest('GET', GS_HOST + 'play/serverparameters/params');
let serverParameters = response.responseText.split("=")[1].split(";")[0].trim();
GS_USERINFO = JSON.parse(serverParameters)["user:info"];
} catch (e) { // 4)
log("fn getGSUserInfo - fetching from play/serverparameters/params unsuccessful --> fetch from play/dataparameters/datalayer");
try {
let response = await promiseRequest('GET', GS_HOST + 'play/dataparameters/datalayer');
let datalayer = response.responseText.split("=")[1].split(";")[0].trim();
GS_USERINFO.userType = JSON.parse(datalayer)[0].membershipLevel;
GS_USERINFO.isLoggedIn = GS_USERINFO.userType != null;
// dateFormat and unitSetName need to be fetched separately
response = await promiseRequest('GET', GS_HOST + 'account/settings/preferences');
GS_USERINFO.dateFormat = $('select#SelectedDateFormat option:selected', response.responseText).val();
GS_USERINFO.unitSetName = $('input#DistanceUnits:checked', response.responseText).val();
} catch (e) { // 4)
log("fn getGSUserInfo - failed to retrieve user info");
GS_USERINFO.dateFormat = "";
GS_USERINFO.userType = "";
GS_USERINFO.isLoggedIn = "";
GS_USERINFO.unitSetName = "";
}
}
}
}
}
}
log_timeEnd('getGSUserInfo');
}
// when uploading to GCTour server in order to store into db, then
// - replace all empty string entries by " "
// - replace all empty float entries by "0.0"
// - replace all int index entries by corresponding string
// - remove all emojis from name entries
// otherwise GCTour server tries to insert NULL into db or values get invalid --> error
function handleEmptyValuesForUpload(obj) {
// see gctour.sql for all available db entries
let strArr = ["id","gcid","guid","name","type","wptcode","lookup","note","content","image","symbol","prefix"];
let floatArr = ["difficulty","terrain","latitude","longitude"];
let intArr = ["index"];
// see https://www.regextester.com/106421
let emojiRange = /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/g;
for (var k in obj) {
if (typeof obj[k] == "object" && obj[k] !== null) handleEmptyValuesForUpload(obj[k]); // recursion
else if (!obj[k] && strArr.includes(k)) obj[k] = " ";
else if (!obj[k] && floatArr.includes(k)) obj[k] = "0.0";
else if (intArr.includes(k)) obj[k] = obj[k].toString();
else if (k === "name") {
obj[k] = obj[k].replace(emojiRange,'').trim(); // remove all emojis from names
if (obj[k].length === 0) obj[k] = " "; // special case: name was all emojis
}
}
return obj;
}
// copy to clipboard
function copyToClipboard(str) {
const el = document.createElement('textarea');
el.value = str;
document.body.appendChild(el);
el.style.position = 'fixed';
el.style.left = '-9999px';
el.select();
document.execCommand('copy');
document.body.removeChild(el);
}
// delay code execution: await sleep(1000)
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// main (don't run on frames or iframes)
if (window.top !== window.self) return;
(async function main() {
try {
// load time
log_timeStart('init main');
// wait until retrieval of user info is finished
await getGSUserInfo();
// init the core components (first tour, current tour)
initCore();
init();
// load time
log_timeEnd('init main');
// check for script update
update();
// get token for the WebAPI
// - either from current page (cache listings contain the token)
// - or from account settings page
TOKEN = $('input[name=__RequestVerificationToken]').val();
if (!TOKEN) {
let resp = await promiseRequest('GET', GS_HOST + 'account/settings/profile');
let parser = new DOMParser();
let doc = parser.parseFromString(resp.responseText, 'text/html');
TOKEN = doc.querySelector('input[name="__RequestVerificationToken"]').value;
}
if (IS_GREASEMONKEY && GM_getValue('gm_notice', 'null') === 'null') { // not set
GM_setValue('gm_notice', false);
gmNotice();
}
} catch (e) {
addErrorDialog({
caption: "error in main",
_exception: e
});
}
})();
})(); // end of of immediately-invoked function expression

Comments are disabled for this gist.