WME KeepMyLayers

Resets WME layers to your preferred state plus other fancy stuff.

当前为 2016-01-10 提交的版本,查看 最新版本

// ==UserScript==
// @name            WME KeepMyLayers
// @description     Resets WME layers to your preferred state plus other fancy stuff.
// @namespace       https://greasyfork.org/users/11629-TheLastTaterTot
// @version         0.3.1
// @include         https://editor-beta.waze.com/*editor/*
// @include         https://www.waze.com/*editor/*
// @exclude         https://www.waze.com/user/*editor/*
// @author          TheLastTaterTot
// @grant           GM_getValue
// @grant           GM_setValue
// @grant           GM_deleteValue
// @run-at          document-start
// ==/UserScript==

// require         https://gist.githubusercontent.com/arantius/3123124/raw/grant-none-shim.js
(function() {
	console.log('<start>');

	function kmllog(str, _) {
		console.log('WMEKMLayers:', str);
	}
	var kmlayersBetaChk = GM_getValue("WMEKMLayers_Beta"),
		kmlayersLangChk = GM_getValue("WMEKMLayers_Lang"),
		kmlPLhost = false,
		kmlPLpath = false;

	if (kmlayersBetaChk === true || kmlayersBetaChk === false ||
		kmlayersLangChk === true) {
		if (kmlayersBetaChk === true && //if set to beta-editor
			location.host.indexOf('editor-b') === -1) { //if PL is not editor-beta.waze.com
			kmlPLhost = 'https://editor-beta.waze.com';
		} else if (kmlayersBetaChk === false && //if set to production editor
			location.host.indexOf('www.waze') === -1) { //if PL is not www.waze.com
			kmlPLhost = 'http://www.waze.com';
		}

		// Check for language
		if (kmlayersLangChk === true) { //if set to filter out language
			if (location.pathname != '/editor/') {
				kmlPLpath = '/editor/';
			}
		}

		switch (true) {
			case (!kmlPLhost && !kmlPLpath): //no modifications are necessary
				break;
			default:
				if (!kmlPLhost) kmlPLhost = location.origin;
				if (!kmlPLpath) kmlPLpath = location.pathname;

				//mllog(location.origin, '/');
				//kmllog(location.pathname, '/');
				//kmllog(location.href, '/');
				//kmllog('Redirecting to ' + kmlPLhost + kmlPLpath + location.href.substring(
				//	location.href.indexOf('?'), location.href.length), '/');

				location.replace(kmlPLhost + kmlPLpath + location.href.substring(
					location.href.indexOf('?'), location.href.length));
		}
	}

	document.onreadystatechange = function() {
		//kmllog('<' + document.readyState + '>', '/');

		if (document.readyState === 'interactive') {
			//kmllog('Inside DOM interactive event interval.', '/');
			//==========================================================================
			// Quickly check to replace layer settings saved by last visited PL
			setTimeout(function() { // async
				if (localStorage.WME_KMLSettings !== undefined &&
					localStorage.WME_KMLSettings.indexOf('&x') !== -1) { // user has set WMEKMLayers to be disabled
					console.log("WMEKMLayers:",
						"Autocheck of KeepMyLayers at startup is disabled (1).");
					return false;
				} else { // primary parent layers
					if (localStorage.WME_KeepMyLayers_lV) {
						sessionStorage.layerVisibility_orig = localStorage.layerVisibility; // save a duplicate as backup
						localStorage.layerVisibility = localStorage.WME_KeepMyLayers_lV;
					} // children
					if (localStorage.WME_KeepMyLayers_lF) {
						sessionStorage.layerFilters_orig = localStorage.layerFilters; // save a duplicate as backup
						localStorage.layerFilters = localStorage.WME_KeepMyLayers_lF;
					}
					//kmllog('Autocheck complete and saved layers were applied (1).', '/');
				}
			}, 0);
		}; //document.readystate
	}; //document.onreadystatechange

	// -----------------------------------------------------------------------------
	var KeepMyLayers = function() {
		var kml = Array(10).fill(0), //counter
			kml_W_map_layers = unsafeWindow.Waze.map.layers,
			$ = unsafeWindow.$;
		//kmlShiftKey = false,
		//kmlAltKey = !!(location.host.indexOf('editor-b') + 1); //if beta, then true

		//==========================================================================
		/*		var permalinkEventListener = function() {
					$("div > a:first-of-type.fa-link").on("hover.kmlayers", function() {
						var checkAltKeyFunc = function() {
							if (kmlAltKey) { //if alt key toggle = true, then beta-editor
								$("div > a:first-of-type.fa-link").attr("href",
									"https://editor-beta.waze.com" + location.pathname + location.href
									.slice(location.href.indexOf('?')));
							} else { // else, production editor
								$("div > a:first-of-type.fa-link").attr("href",
									"https://www.waze.com" + location.pathname + location.href
									.slice(location.href.indexOf('?')));
							}
						};
						checkAltKeyFunc();
						// listen for alt key toggle
						$("div > a:first-of-type.fa-link").keydown(function(event) {
							if (event.which == 18) kmlAltKey = !kmlAltKey; //toggle on keydown
							checkAltKeyFunc();
						});

					}, function() {
						// Stuff to do when the mouse leaves the element
					});
				};
		*/
		//==========================================================================
		var getSavedKMLayers = function() {
			//kmllog('getSavedKMLayers()', '/');
			var myKMLayers;

			if (typeof myKMLayers !== "undefined") {
				if (myKMLayers.visibleInLayersMenu !== undefined) {
					//kmllog('Replaced myKMLayers in memory with saved from disk.', '/');
					myKMLayers = JSON.parse(localStorage.WME_KeepMyLayers);
					myKMLayers.reset = false;
					return myKMLayers;
				} else {
					return false;
				}
			} else if (localStorage.WME_KeepMyLayers !== undefined) {
				//kmllog('Found some saved settings to load!','/');
				myKMLayers = JSON.parse(localStorage.WME_KeepMyLayers);
				myKMLayers.reset = false;

				if (myKMLayers.visibleInLayersMenu !== undefined) { //check that required object key is present
					return myKMLayers;
				} else {
					return false;
				}
			} else { // myKMLayers has not been saved yet
				/* TODO: Call to popup of instructions for saving default layers set will maybe go here */
				return false;
			}
		}

		//W.map.layers.forEach(function(a){console.debug(a["name"] + ': ' + a["visibility"])});
		var getWazeMapLayersFromSwitcher = function(kmlayers) { // get OL layers that show up under the WME Layer switcher panel
			var kml_lay = kmlayers.length,
				kml_layerName, kml_layerUniqName,
				kml_layerSwitcher = {
					accelerator: {},
					uniqueName: {}
				};

			while (kml_lay--) {
				kml_layerName = kmlayers[kml_lay].accelerator;
				kml_layerUniqName = kmlayers[kml_lay].uniqueName;
				if (kml_layerName !== undefined) { //  accelerator is a marker with high specificity and selectivity of whether the layer appears in the layer switcher menu
					if (kml_layerUniqName !== undefined) { // uniqueName is used by WME for resetting layers to last PL
						kml_layerSwitcher.uniqueName[kml_layerUniqName] = kml_lay;
						kml_layerSwitcher.accelerator[kml_layerName] = kml_lay;
					} else {
						/*kmllog('Note - No uniqueName found for "' +
							kmlayers[kml_lay].name +
							'"', '/');*/
						kml_layerSwitcher.accelerator[kml_layerName] = kml_lay;
					}
				}
			}
			return kml_layerSwitcher;
		};
		// -------------------------------------------------------------------------
		var saveKMLayers = function() {
			//kmllog('saveKMLayers()', '/');
			var kml_j, kml_lname,
				kml_layerSwitcher = getWazeMapLayersFromSwitcher(kml_W_map_layers),
				kml_layerNames = Object.keys(kml_layerSwitcher.accelerator),
				kml_numLayers = kml_layerNames.length,
				visibleInLayersMenu = {};


			// Get names of visible menu layers
			for (kml_j = kml_numLayers; kml_j--;) {
				kml_lname = kml_layerNames[kml_j];
				if (kml_W_map_layers[kml_layerSwitcher.accelerator[kml_lname]].getVisibility()) {
					visibleInLayersMenu[kml_lname] = true;
				}
			}

			// Save to localStorage
			localStorage.WME_KeepMyLayers_lV = localStorage.layerVisibility;
			if (localStorage.layerFilters) {
				localStorage.WME_KeepMyLayers_lF = localStorage.layerFilters;
			} else {
				localStorage.WME_KeepMyLayers_lF =
					'{"mapProblem":0,"mapUpdateRequest":0,"venue":0}';
			}
			myKMLayers = {
				visibleInLayersMenu: visibleInLayersMenu
			};
			localStorage.WME_KeepMyLayers = JSON.stringify(myKMLayers);
			$('#layer-switcher-list').after(
				'<div id="KMLpopup" style="font-weight: bold; color: #DD3300; position: absolute; display: block; bottom: -30px; right: 14px">Saved &lt;</div>'
			);

			$("#iKMLsaveLayers").removeClass("kml-icn-nsave");
			//document.getElementById('iKMLsaveLayers').className = document.getElementById('iKMLsaveLayers').className.replace(' kml-icn-nsave','');

			setTimeout(function() {
				$('#KMLpopup').remove();
			}, 800);
			//return myKMLayers;
		};


		var enableUndoKMLayersReset = function() {
			//kmllog('enableUndoKMLayersReset()', '/');
			// the following blocks of code needs cleaning up....
			if (document.getElementById('iKMLtempUndo') !== null &&
				$("a.kml-icn").length < 4) {
				kml[2] = 0; //reset counter

				if (typeof myKMLayers !== "undefined" &&
					myKMLayers.reset !== undefined && myKMLayers.reset === true) {
					var kml_layerVisibility_orig = JSON.parse(sessionStorage.layerVisibility_orig),
						kml_layerVisibility_kmlayers = JSON.parse(localStorage.WME_KeepMyLayers_lV),
						kml_layerNames = Object.keys(kml_layerVisibility_orig),
						kml_numlnames = kml_layerNames.length,
						kml_toggledLayers = [];

					kml[3] = 0;
					while (kml_numlnames--) {
						if (kml_layerVisibility_orig[kml_layerNames[kml_numlnames]] !==
							kml_layerVisibility_kmlayers[kml_layerNames[kml_numlnames]]
						) {
							kml_toggledLayers[kml[3]++] = kml_layerNames[kml_numlnames];
						}
					}

					var nToggled = kml_toggledLayers.length;
					if (nToggled !== 0) {
						//kmllog('Layer visibilities were reset to preferred setting.', '/');
						$('#iKMLtempUndo').wrap(
							'<a href="javascript:void(0)" class="kml-icn"></a>');
						$('#iKMLtempUndo').toggleClass("kml-icn-off")

						document.getElementById("iKMLtempUndo").onclick = function() {
							var kml_tog = nToggled;
							while (kml_tog--) {
								kml_W_map_layers[myKMLayers.uniqueName[kml_toggledLayers[kml_tog]]]
									.setVisibility(!kml_W_map_layers[myKMLayers.uniqueName[
										kml_toggledLayers[kml_tog]]].visibility);
							}
						};
					} // else do nothing, as there is nothing to undo bc layers were not reset
				}
			} else if ($("a.kml-icn").length > 3) {
				kml[2] = 0;
				return true; //button already enabled
			} else if (kml[2]++ < 30) {
				setTimeout(enableUndoKMLayersReset, 200);
			} else {
				console.warn('WMEKMLayers:',
					'Unable to activate "Undo KMLayers Reset".',
					'Element #iKMLtempUndo not found on page.')
			}
		};

		//----------------------------------------------------------------------
		/* ["toggleLiveUsers", "toggleRouteCheckerScript", "toggleJunctionAngleInfo",
		"toggleWMEClosestSegment", "toggleRoadClosures", "toggleEditableAreas",
		"toggleUpdateRequests", "toggleMapProblems", "toggleSpeedcameras", "toggleJunctionboxes",
		"togglePlaceupdates", "togglePlaces", "toggleAreaManagers", "toggleGPSpoints",
		"toggleRoads", "toggleCities", "toggleSatelliteImagery"] */
		var checkForReqLayers = function(permalink) {
			switch (true) {
				case permalink.indexOf('&mapUp') !== -1:
					return "toggleUpdateRequests";
				case permalink.indexOf('&ven') !== -1:
					return "togglePlaces";
				case permalink.indexOf('&mapPr') !== -1:
					return "toggleMapProblems";
				case permalink.indexOf('&bigJ') !== -1:
					return "toggleJunctionboxes";
				case permalink.indexOf('&cam') !== -1:
					return "toggleSpeedcameras";
				default:
					return false;
			}
		};

		//----------------------------------------------------------------------
		var resetLayersToSavedKMLayers = function(kml_exclude) {
			//kmllog('resetLayersToSavedKMLayers(' + kml_exclude + ')', '/');
			if (typeof myKMLayers !== "undefined") {
				var kml_layerSwitcher = getWazeMapLayersFromSwitcher(
						kml_W_map_layers),
					accelNamesObj = kml_layerSwitcher.accelerator,
					accelNames = Object.keys(accelNamesObj),
					kml_j = accelNames.length,
					kml_lname,
					visibleInLayersMenu;

				// Adjust layer visibility if necessary
				visibleInLayersMenu = JSON.parse(JSON.stringify(myKMLayers.visibleInLayersMenu));

				if (kml_exclude) visibleInLayersMenu[kml_exclude] = true;

				while (kml_j--) {
					kml_lname = accelNames[kml_j];
					kml_W_map_layers[accelNamesObj[kml_lname]].setVisibility(!!
						visibleInLayersMenu[kml_lname]);
				}

				myKMLayers.reset = true;
				myKMLayers.uniqueName = kml_layerSwitcher.uniqueName;

				setTimeout(enableUndoKMLayersReset, 500);
				localStorage.WME_KeepMyLayers_lV = localStorage.layerVisibility;
				if (localStorage.layerFilters) {
					localStorage.WME_KeepMyLayers_lF = localStorage.layerFilters;
				} else {
					localStorage.WME_KeepMyLayers_lF =
						'{"mapProblem":0,"mapUpdateRequest":0,"venue":0}';
				}
			}
		};

		// ---------------------------------------------------------------------
		var applyAdditionalKMLSettings = function() {
			//kmllog('applyAdditionalKMLSettings()', '/');
			// ['&x', '&b', '&l', '&1', '&5', '&2']; //disable, beta, lang, city, am, roads
			if (localStorage.WME_KMLSettings.indexOf('&1') !== -1) {
				kml_W_map_layers[1].setOpacity(1);
				//kmllog('Opacity of Cities layer increased.', '/');
			} else {
				kml_W_map_layers[1].setOpacity(0.5);
				//kmllog('Opacity of Cities layer at default.', '/');
			}

			if (localStorage.WME_KMLSettings.indexOf('&5') !== -1) {
				kml_W_map_layers[5].setOpacity(0.6);
				//kmllog('Opacity of AM layer decreased.', '/');
			} else {
				kml_W_map_layers[5].setOpacity(1);
				//kmllog('Opacity of AM layer at default.', '/');
			}

			if (localStorage.WME_KMLSettings.indexOf('&2') !== -1) {
				kml_W_map_layers[2].setOpacity(0.72);
				//kmllog('Opacity of Roads layer decreased.', '/');
			} else {
				kml_W_map_layers[2].setOpacity(1);
				//kmllog('Opacity of Roads layer at default.', '/');
			}

		};
		// ---------------------------------------------------------------------
		function runSecondaryKMLayersCheck() {
			//kmllog('runSecondaryKMLayersCheck()', '/');
			if (localStorage.WME_KMLSettings === undefined) {
				localStorage.WME_KMLSettings = ':-';
			}

			if (localStorage.WME_KMLSettings.indexOf('&x') !== -1) { // user has set WMEKMLayers to be disabled
				console.log('WMEKMLayers:',
					'Autocheck of KeepMyLayers at startup is disabled (2).');
				return false;
			} else {
				var myKMLayers = getSavedKMLayers();
				//kmllog('Secondary check completed. Any saved layers will be applied (2).', '/');
				if (myKMLayers) {
					return myKMLayers;
				} else {
					return false;
				}
			}
		}

		//======================================================================
		var myKMLayers = runSecondaryKMLayersCheck();
		//KML = myKMLayers;
		if (myKMLayers) {
			resetLayersToSavedKMLayers(checkForReqLayers(location.href));
		}
		applyAdditionalKMLSettings();

		//======================================================================
		var userResetOfLayersToSavedKMLayers = function() {
			//kmllog('userResetOfLayersToSavedKMLayers()', '/');
			if (getSavedKMLayers()) {
				resetLayersToSavedKMLayers(false);
			} else {
				//kmllog('Nothing to reset.', '/');
				return false;
			}
		};

		//----------------------------------------------------------------------
		// Beta Editor Toggle Button
		var insertKMLBetaToggle = function(kmlBetaToggle) {
			//kmllog('insertKMLBetaToggle()', '/');
			if ($('#user-details').length !== 0 && $('#cbKMLtoggle').length === 0) {
				kml[4] = 0; //reset counter

				//kmllog('kmlBetaToggle (before) = ' + kmlBetaToggle, '/');
				if (kmlBetaToggle === undefined) {
					kmlBetaToggle = !!GM_getValue("WMEKMLayers_Beta");
				}

				(kmlBetaToggle) ? kmlBetaToggle = 'checked': kmlBetaToggle = '';
				//kmllog('kmlBetaToggle (after) = ' + kmlBetaToggle, '/');

				$('#user-details').prepend(
					'<div class="kml-toggle-container"><div class="kml-toggle">' +
					'<input type="checkbox" name="kml-toggle" class="kml-toggle-checkbox" id="cbKMLtoggle"' +
					kmlBetaToggle + '>' +
					'<label class="kml-toggle-label" for="cbKMLtoggle">' +
					'<span class="kml-toggle-inner"></span>' +
					'<span class="kml-toggle-switch"></span></label></div></div>');

				document.getElementById('cbKMLtoggle').onclick = function() {
					setTimeout(function() {
						GM_setValue("WMEKMLayers_Beta",
							document.getElementById('cbKMLtoggle').checked);
					}, 0)
				};
			} else if ($('#cbKMLtoggle').length > 0) {
				kml[4] = 0;
				return true;
			} else if (kml[4]++ < 20) {
				setTimeout(insertKMLBetaToggle, 400);
			} else {
				console.warn('WMEKMLayers:',
					'Unable to insert "Beta-Editor Toggle".',
					'Element #user-details not found on page.')
			}
		};

		//----------------------------------------------------------------------
		var showKMLSettingsPanel = function() {
			//kmllog('showKMLSettingsPanel()', '/');
			// setup dimensions of settings box
			var kmlInitStatus = !!(location.host.indexOf('editor-beta') + 1);

			var applyKMLayersSettings = function() {
				//kmllog('cbKML_0 checked? ' + document.getElementById("cbKML_0").checked,'/');
				if (document.getElementById("cbKML_0").checked) {
					setTimeout(function() {
						GM_setValue("WMEKMLayers_Beta", kmlInitStatus)
					}, 0);
					/*kmllog(
						'Beta toggle selected... adding var into GM scope and page.', '/')*/
					insertKMLBetaToggle(kmlInitStatus);
				} else {
					/*kmllog(
						'Beta toggle not selected... removing var from GM scope.', '/')*/
					setTimeout(function() {
						GM_deleteValue("WMEKMLayers_Beta")
					}, 0);
					document.getElementsByClassName('kml-toggle-container')[0].remove();
					//kmllog('WMEKMLayers_Beta = ' + GM_deleteValue("WMEKMLayers_Beta"), '/')
				}

				//kmllog('cbKML_1 checked? ' + document.getElementById("cbKML_1").checked,'/');
				if (document.getElementById("cbKML_1").checked) {
					/*kmllog(
						'Remove language specs from PL... adding var to GM scope.', '/')*/
					setTimeout(function() {
						GM_setValue("WMEKMLayers_Lang", true)
					}, 0);
				} else {
					/*kmllog(
						'Not removing lang specs from PL... var no longer in GM scope.', '/'
					)*/
					setTimeout(function() {
						GM_deleteValue("WMEKMLayers_Lang")
					}, 0);
				}

				var storeStr = ':-',
					kmlOptions, kmlSettingsUI;

				if ($('#cbKML_5')[0].checked) storeStr += '&x'; //disable KML
				if ($('#cbKML_0')[0].checked) storeStr += '&b'; //beta
				if ($('#cbKML_1')[0].checked) storeStr += '&l'; //lang
				if ($('#cbKML_2')[0].checked) storeStr += '&1'; //city
				if ($('#cbKML_3')[0].checked) storeStr += '&5'; //am
				if ($('#cbKML_4')[0].checked) storeStr += '&2'; //roads

				localStorage.WME_KMLSettings = storeStr;

				applyAdditionalKMLSettings();

				$('#KMLsettings').remove();
			};

			// setup checkbox list for UI
			kmlOptions = [
				'Show WME Beta Editor toggle in the left side-panel', //[0]
				'Remove language specifications from clicked permalinks', //[1]
				'Increase the opacity of city polygons to make them more visible', //[2] layer 1 - 1city
				'Make area manager polygons more translucent', //[3] layer 5 - 5am
				'Add a small amount of transparency to Roads layer', //[4] layer 2 - 2roads
				'Disable checking layers at startup'
			]; //[5] xx

			kmlSettingsUI =
				'<div id="KMLsettings" class="kml-panel-blackout"><div class="kml-panel">' +
				'<i id="iKMLsettings" class="icon-cog icon-4x pull-left fa fa-cog fa-4x fa-pull-left"></i>' +
				'<h2>KeepMyLayers Preferences</h2>' +
				'<hr class="kml-panel-hr">' +
				'<div class="kml-panel-section">';

			//TODO: Clean the following code up....
			var selectedKMLSettings = Array(kmlOptions.length),
				allStr = ['&b', '&l', '&1', '&5', '&2', '&x'], //beta, lang, city, am, roads, disable
				kmlayersBetaChk = GM_getValue("WMEKMLayers_Beta"),
				kmlayersLangChk = GM_getValue("WMEKMLayers_Lang"),
				cbKMLSettingsStr, kml_s;

			// Check for beta toggle setting in two locations for consistency
			if (localStorage.WME_KMLSettings === undefined) {
				localStorage.WME_KMLSettings = ':-';
				cbKMLSettingsStr = ':-';
			} else {
				cbKMLSettingsStr = localStorage.WME_KMLSettings;
			}

			// Ensure consistency between vars in GM scope and settings saved
			//  in localStorage:
			//	...Beta Toggler
			if (localStorage.WME_KMLSettings.indexOf("&b") === -1) { // if not a saved setting in localStorage
				if (kmlayersBetaChk === true || kmlayersBetaChk === false) { // but var exists in GM and defined as either T/F
					localStorage.WME_KMLSettings += "&b";
					cbKMLSettingsStr += "&b";
				}
			}
			//  ...Language specification in permalinks
			if (localStorage.WME_KMLSettings.indexOf("&l") === -1) { // if not a saved setting in localStorage
				if (kmlayersLangChk === true) { // but var exists in GM scope and is TRUE
					localStorage.WME_KMLSettings += "&l";
					cbKMLSettingsStr += "&l";
				}
			}
			//  Setup KMLayers settings panel
			for (kml_s = 0; kml_s < kmlOptions.length; kml_s++) {
				(cbKMLSettingsStr.indexOf(allStr[kml_s]) !== -1) ?
				selectedKMLSettings[kml_s] = ' checked': selectedKMLSettings[kml_s] =
					' ';
				kmlSettingsUI += '<div class="controls-container">' +
					'<input type="checkbox" id="cbKML_' + kml_s +
					'" class="btn btn-default"' + selectedKMLSettings[kml_s] +
					'></input>' +
					'<label for="cbKML_' + kml_s + '">' + kmlOptions[kml_s] +
					'</label></div>';
			}
			kmlSettingsUI += '</div>' +
				'<div style="font-size: 9pt; font-style: italic; margin-left: 35px; margin-top: 0px; margin-bottom: 0px;">Does not include any of the above settings</div>' +
				'<hr class="kml-panel-hr">' +
				'<div class="kml-panel-btn"><div style="display: inline-block; margin-left: 0px;">' +
				'<button id="btnKMLsave" style="width: 70px" class="btn btn-primary kml-panel-btn">Save</button>' +
				'<button id="btnKMLcancel" class="btn btn-default kml-panel-btn">Cancel</button></div>' +
				'<div id="KMLnote" style="color: #81B5C6; position: absolute; right: 5px; display: inline-block; text-align: right; ' +
				'						width: 300px; line-height: 15px; height: 40px; vertical-align: middle"></div></div></div></div>';
			$('#map').append(kmlSettingsUI);

			kmlSettingsUI = null;

			if (!getSavedKMLayers()) {
				$('#KMLnote').append(
					'<span style="font-size:10px">To save your default set of layers, select your layers under the Layer Switcher menu, then click' +
					'<div style="color: #48758C; font-style: normal; margin-left: 4px; display:inline-block" class="icon-save fa fa-save kml-icn-nsave"></div> at the bottom-right.</span>'
				);
			}

			document.getElementById('btnKMLsave').onclick = applyKMLayersSettings;

			document.getElementById('btnKMLcancel').onclick = function() {
				document.getElementById("KMLsettings").remove();
			};
		};

		//======================================================================
		var initKMLButtons = function() {
			//kmllog('inside initKMLButtons()', '/');

			if (document.getElementById("layer-switcher-list") &&
				document.getElementById("iKMLsaveLayers") === null) {
				kml[6] = 0; //reset counter
				var panelHeight = 350,
					panelWidth = 530,
					kmlStyle = document.createElement("style");

				// Create CSS container element
				kmlStyle.type = "text/css";
				kmlStyle.id = "kml-css-container";

				// CSS for KMLayers icons under Layers dropdown menu
				kmlStyle.innerHTML =
					'div.kml-icn { position: absolute; display: inline-block; vertical-align: middle; ' +
					'				bottom: 0px; right: 0px; padding: 0; margin: 0px 10px 10px 0px; }\n' +
					'.kml-icn-nsave { font-weight: 400; color: #D36343 !important; } \n' + //	'.kml-icn-save { position: relative; display: block; bottom: 45px; right: 0px }\n' +
					'.kml-icn-btn {	position: relative; display: inline-block; padding-left: 4px; padding-right: 4px; }\n' + //right: 30px; top: 10px; 'width: 12px; height: 12px;
					'.kml-icn:active, .kml-icn:focus, .kml-icn:hover, .kml-icn.active { ' +
					'				text-decoration: none; background-image: none; outline: 0; ' +
					'				-webkit-box-shadow: none; box-shadow: none; cursor: pointer }\n' +
					'.kml-icn-off, .kml-icn-off:hover, .kml-icn-off:focus { ' +
					'				color: #B1D4DF !important; cursor: default !important; }\n' +
					'span.kml-icn-btn { padding: 0; margin: 0px 10px 10px 0px; }\n';

				// CSS for settings panel
				kmlStyle.innerHTML +=
					'div.kml-panel-blackout { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; ' +
					'				background: rgba(0,0,0,0.5); z-index: 2000; }\n' +
					'.kml-panel { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); ' +
					'				width: ' + panelWidth + 'px; height: ' + panelHeight + 'px; ' +
					'				padding: 10px; margin: 0; overflow-y: auto; overflow-x: auto; word-wrap: break-word; ' +
					'				background-color: white; box-shadow: 0px 5px 20px #555555; ' +
					'				border: 1px solid #858585; border-radius: 10px; }\n' +
					'.kml-panel h2 { margin-top:10px; margin-bottom: 10px; font-size: 20pt; font-weight: bold; text-align: left; color: #C0C0C0 }\n' +
					'div.kml-panel-section {display: block; font-size: 12pt; text-align: left }\n' +
					'.kml-panel-hr { display: block; border: 0; height: 0; border-top: 1px solid rgba(0, 0, 0, 0.1); ' +
					' 				border-bottom: 1px solid rgba(255, 255, 255, 0.3); ' +
					'				margin-top: 8px; margin-bottom: 12px; }\n' +
					'.kml-panel-btn { margin: 5px 5px 0px; padding: 0px 15px; display: inline-block; height: 32px; }\n' +
					'div.kml-panel-btn {display: block; position: relative; padding: 0; width: 480px; margin: auto; ' +
					'				vertical-align: middle; height: 40px; }\n';

				// CSS for beta-editor toggle
				kmlStyle.innerHTML +=
					'.kml-toggle-container { position: absolute; right: 10px; top: 10px; }\n' +
					'.kml-toggle { position: relative; width: 40px; ' +
					'				-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; }\n' +
					'.kml-toggle-checkbox { display: none; }\n' +
					'.kml-toggle-label { display: block;  overflow: hidden; cursor: pointer;  border: 2px solid #CDCDCD; border-radius: 20px; }\n' +
					'.kml-toggle-inner { display: block; width: 200%; margin-left: -100%; transition: margin 0.2s ease-in 0s; }\n' +
					'.kml-toggle-inner:before, .kml-toggle-inner:after {  ' +
					'				display: block; float: left; width: 50%; height: 20px;  ' +
					'				padding: 0; line-height: 20px; font-size: 10px; box-sizing: border-box; }\n' +
					'.kml-toggle-inner:before { content: ""; padding-left: 5px; background-color: #FC6C70; }\n' +
					'.kml-toggle-inner:after { content: ""; padding-right: 5px; background-color: #CDCDCD; }\n' +
					'.kml-toggle-switch { display: block; width: 24px; margin: 0px;  ' +
					'				position: absolute; top: 0; bottom: 0; right: 16px; background: #FFFFFF; ' +
					'				border: 2px solid #CDCDCD; border-radius: 20px; transition: all 0.2s ease-in 0s; }\n' +
					'.kml-toggle-checkbox:checked + .kml-toggle-label .kml-toggle-inner { margin-left: 0; }\n' +
					'.kml-toggle-checkbox:checked + .kml-toggle-label .kml-toggle-switch { right: 0px; }\n';

				document.body.appendChild(kmlStyle);
				kmlStyle = null;

				// Add save layers icon to Layers switcher dropdown panel
				var kmlDiv = document.createElement("div");
				kmlDiv.className = "kml-icn";
				kmlDiv.innerHTML =
					'<a href="javascript:void(0)" class="kml-icn"><div id="iKMLsaveLayers" class="icon-save fa fa-save kml-icn-btn reload-button" data-toggle="tooltip" title="Save Current Layers As Defaults"></div></a>' +
					'<a href="javascript:void(0)" class="kml-icn"><div id="iKMLsettings" class="icon-cog fa fa-cog kml-icn-btn reload-button" data-toggle="tooltip" title="Preferences"></div></a>' +
					'<a href="javascript:void(0)" class="kml-icn"><div id="iKMLresetLayers" class="icon-magic fa fa-magic kml-icn-btn reload-button" data-toggle="tooltip" title="Reset Layers to My Default Set"></div></a>' +
					'<div id="iKMLtempUndo" class="icon-eye-open fa fa-eye kml-icn-btn reload-button kml-icn-off" data-toggle="tooltip" title="Toggle Layers from Permalink"></div>';

				document.getElementById("layer-switcher-list").parentNode.insertBefore(
					kmlDiv, document.getElementById("layer-switcher-list").nextSibling);
				kmlDiv = null;

				// Setup event listeners
				document.getElementById("iKMLsaveLayers").onclick = saveKMLayers;
				document.getElementById("iKMLsettings").onclick = showKMLSettingsPanel;
				document.getElementById("iKMLresetLayers").onclick =
					userResetOfLayersToSavedKMLayers;
				$(".kml-icn-btn[data-toggle=tooltip]").tooltip();
				//{	placement: 'bottom'	}

				var kmlInitStatus = GM_getValue("WMEKMLayers_Beta");
				//kmllog('WMEKMLayers_Beta = ' + kmlInitStatus, '/');
				if (kmlInitStatus !== undefined &&
					kmlInitStatus !== null) {
					insertKMLBetaToggle(kmlInitStatus);
				}
				//kmllog('Checking for localStorage.WME_KeepMyLayers_lV');
				setTimeout(function() {
					if (localStorage.WME_KeepMyLayers_lV === undefined) {
						document.getElementById("iKMLsaveLayers").className =
							document.getElementById('iKMLsaveLayers').className +
							' kml-icn-nsave';
						//$("#iKMLsaveLayers").addClass("kml-icn-nsave");
					}
					//kmllog('Done checking for localStorage.WME_KeepMyLayers_lV');
				}, 300);

			} else if (document.getElementById("iKMLsaveLayers") !== null) {
				kml[6] = 0;
				return true; //icons already inserted into page
			} else if (kml[6]++ < 20) {
				setTimeout(initKMLButtons, 500);
			} else {
				console.warn('WMEKMLayers:',
					'Unable to insert WME KeepMyLayers under Layers menu.',
					'Element #layer-switcher-list not found on page.')
			}
		};

		//kmllog('Stepping into initKMLButtons()...', '/');
		initKMLButtons();


		// ~~~ TEMP (clean-up) ~~~
		localStorage.removeItem("WME_KML_Settings");
		GM_deleteValue("WMEKML_Beta");
		GM_deleteValue("WMEKML_Lang");
		//kmllog('Done performing cleanup')
		// ~~~~~~~~~~~~~~~~~~~~~~~

		if (localStorage.WME_KMLSettings.indexOf(':-') === -1) { // Display one-time message
			showKMLSettingsPanel();

			var kmlNoticeUI = document.createElement("div");
			kmlNoticeUI.className = "kml-panel";
			kmlNoticeUI.id = "divKMLalert";
			kmlNoticeUI.style.width = "500px";
			kmlNoticeUI.style.height = "270px";
			kmlNoticeUI.innerHTML =
				'<i id="iKMLsettings" class=" icon-exclamation-sign icon-4x pull-left fa fa-exclamation-circle fa-4x fa-pull-left"></i>' +
				'<h2>WME KMLayers Update</h2>' +
				'<hr class="kml-panel-hr">';
			kmlNoticeUI.innerHTML +=
				'<div class="kml-panel-section" style="font-size: 10pt">' +
				'Thanks for everyone\'s input! KeepMyLayers has been updated to version 0.3.1 to work with both latest stable versions of Chrome and Firefox. ' +
				'Several enhancements and bug-fixes were made. ' +
				'A faster URL and layer interception method has been implemented, which means <b>you must resave your preferences</b>. (Sorry) ' +
				' ' +
				'For the full update list, please visit the forums <a href="https://www.waze.com/forum/viewtopic.php?f=819&t=172335&p=1301210&sid=9c9cc45ed5b8e9ab575506b1a0cf4b59#p1301210">here</a>. ' +
				'<div style="margin-top:10px"> ' +
				'Note: You will not see this message again for this domain, unless you clear your browser\'s local storage. ' +
				'</div></div>';
			kmlNoticeUI.innerHTML += '<hr class="kml-panel-hr">' +
				'<div class="kml-panel-btn" style="position: absolute; display: block; left: 40%; bottom: 0px; width: 60px; margin: 10px; vertical-align: middle; padding: 0">' +
				'<button id="btnKMLokay" style="width: 70px" class="btn btn-primary kml-panel-btn">OK</button></div>';

			document.getElementsByClassName("kml-panel")[0].appendChild(kmlNoticeUI);

			document.getElementById('btnKMLokay').onclick = function() {
				document.getElementById('divKMLalert').remove();
				localStorage.WME_KMLSettings += ':-'
			};
		}
	};

	////////////////////////////////////////////////////////////////////////////
	function waitForWazeMap_KMLayers() {
		var waitCount = 0;
		//kmllog('Waiting for Waze...', '/');

		var tryInit_KMLayers = function() {
			try {
				if (waitCount < 20 &&
					"undefined" !== typeof(unsafeWindow) &&
					unsafeWindow.Waze && unsafeWindow.Waze.map && unsafeWindow.Waze.map.layers
				) {
					//kmllog('Initializing...', '/');
					KeepMyLayers();
				} else if (waitCount++ < 20) {
					setTimeout(tryInit_KMLayers, 200);
				} else {
					//kmllog('waitCount = ' + waitCount, '/');
					//kmllog('typeof Waze = ' + typeof(unsafeWindow.Waze), '/');
					//kmllog('Waze.map = ' + !!unsafeWindow.Waze.map, '/');
					//kmllog('Waze.map.layers = ' + !!unsafeWindow.Waze.map.layers, '/');
					console.error('WMEKMLayers:',
						'KeepMyLayers could not find necessary Waze map objects.');
				}
			} catch (err) {
				console.error(
					'WMEKMLayers:',
					'WME KeepMyLayers failed to load',
					'due to some kind of technical script error. :(');
				console.error(err);
			}
		};
		tryInit_KMLayers();
	};

	////////////////////////////////////////////////////////////////////////////
	var kml_waitTimer = setInterval(
		function() {
			//kmllog('tic', '/');
			if (document.readyState === 'interactive' || document.readyState ===
				'complete') {
				if ("undefined" !== typeof(unsafeWindow) && unsafeWindow.$) {
					//kmllog('boom!', '/');
					clearInterval(kml_waitTimer);
					waitForWazeMap_KMLayers();
				}
			}
		}, 10);

})();