WME Road Selector Highlights

Adds highlighting ability to WME Road Selector. Requires WME Road Selector to function.

目前為 2016-01-10 提交的版本,檢視 最新版本

// ==UserScript==
// @name            WME Road Selector Highlights
// @namespace       https://greasyfork.org/users/11629-TheLastTaterTot
// @description     Adds highlighting ability to WME Road Selector. Requires WME Road Selector to function.
// @include         https://editor-beta.waze.com/*editor/*
// @include         https://www.waze.com/*editor/*
// @exclude         https://www.waze.com/user/*editor/*
// @version         0.5.1.1
// @grant           none
// ==/UserScript==
(function() {

	function RSH() {
		this.USR_EXPR = [{}];
		this.PREFS = {
			strokeColor: ["#FFFF00"],
			strokeOpacity: [0.5],
			strokeDashstyle: [0],
			strokeLinecap: [0],
			strokeWidthScale: [1],
			strokeDashSizeScale: [1],
			strokeSpacingScale: [1]
		};
		this.lastIdx = 0;
		this.idx = 0;
		this.zLoadOrder = [0];
		this.eraseOnScreen_idx = false;
		this.triggerUpdate = true;
		this.showSessionHighlights = false;
		this.loadedSavedSession = false;
		this.presets = {
			linePropObjKeys: [
				'strokeColor',
				'strokeOpacity',
				'strokeDashstyle',
				'strokeLinecap',
				'strokeWidthScale',
				'strokeDashSizeScale',
				'strokeSpacingScale'
			],
			linePropIds: ['selRSHColors', 'numRSHOpacity', 'selRSHDash',
				'selRSHCap', 'numRSHScaleWidth', 'numRSHDashSizeScale',
				'numRSHSpacingScale'
			],
			linePropAttrType: ['value', 'valueAsNumber', 'selectedIndex',
				'selectedIndex', 'valueAsNumber', 'valueAsNumber', 'valueAsNumber'
			],
			defaultLineAttributes: {
				strokeColor: "#FFFF00",
				strokeOpacity: 0.5,
				strokeDashstyle: 0,
				strokeLinecap: 0,
				strokeWidthScale: 1,
				strokeDashSizeScale: 1,
				strokeSpacingScale: 1
			},
			lineAttributes: {
				strokeWidth: [4, 5, 7, 8, 9, 11, 13, 14, 14, 15, 15],
				strokeDashstyle: ["solid", "- - - - -", "– ‧ – ‧"],
				strokeDasharray: [
					["solid"],
					[1, 1],
					[2, 1, 1, 1]
				],
				strokeLinecap: ["butt", "round", "square"]
			}
		};

		this.addLayer = function() {
			this.idx = this.USR_EXPR.length; //next index
			this.USR_EXPR[this.idx] = {};
			this.PREFS.strokeColor[this.idx] = "#FFFF00";
			this.PREFS.strokeOpacity[this.idx] = 0.5;
			this.PREFS.strokeDashstyle[this.idx] = 0;
			this.PREFS.strokeLinecap[this.idx] = 0;
			this.PREFS.strokeWidthScale[this.idx] = 1;
			this.PREFS.strokeDashSizeScale[this.idx] = 1;
			this.PREFS.strokeSpacingScale[this.idx] = 1;
			this.lastIdx = this.idx;
			this.zLoadOrder[this.idx] = this.idx;
			this.triggerUpdate = true;
		};

		this.removeLayer = function() {
			var seq = function(b) {
				return Array.apply(null, Array(b)).map(function(_, c) {
					return c;
				});
			}

			this.USR_EXPR.splice(this.idx, 1);
			this.PREFS.strokeColor.splice(this.idx, 1);
			this.PREFS.strokeOpacity.splice(this.idx, 1);
			this.PREFS.strokeDashstyle.splice(this.idx, 1);
			this.PREFS.strokeLinecap.splice(this.idx, 1);
			this.PREFS.strokeWidthScale.splice(this.idx, 1);
			this.PREFS.strokeDashSizeScale.splice(this.idx, 1);
			this.PREFS.strokeSpacingScale.splice(this.idx, 1);
			((this.idx - 1) > 0) ? this.idx--: this.idx = 0;
			this.lastIdx = this.USR_EXPR.length - 1;
			this.zLoadOrder = seq(this.USR_EXPR.length);
			this.triggerUpdate = true;
		};

		this.housekeep = function() {
			numLayers = this.USR_EXPR.length;
			notEmptyIndexes = elementsWithValues(this.USR_EXPR);
			numNewLayers = notEmptyIndexes.length;

			this.USR_EXPR = removeEmpty(this.USR_EXPR, notEmptyIndexes)
			var p = this.presets.linePropObjKeys.length;

			while (p--) {
				lineProp = this.presets.linePropObjKeys[p];
				//console.log(lineProp)
				this.PREFS[lineProp] = removeEmpty(this.PREFS[lineProp],
					notEmptyIndexes);
			}
			this.zLoadOrder = removeEmpty(this.zLoadOrder, notEmptyIndexes);

			if (numLayers !== numNewLayers) {
				this.lastIdx = numNewLayers - 1;
				this.idx = this.zLoadOrder.indexOf(this.idx);
				if (this.idx === -1) this.idx = this.lastIdx;
				return true;
			} else {
				return false;
			}
		};

		this.importLayers = function(addThis) {
			this.USR_EXPR = addThis.USR_EXPR;
			this.PREFS.strokeColor = addThis.PREFS.strokeColor;
			this.PREFS.strokeOpacity = addThis.PREFS.strokeOpacity;
			this.PREFS.strokeDashstyle = addThis.PREFS.strokeDashstyle;
			this.PREFS.strokeLinecap = addThis.PREFS.strokeLinecap;
			this.PREFS.strokeWidthScale = addThis.PREFS.strokeWidthScale;
			this.PREFS.strokeDashSizeScale = addThis.PREFS.strokeDashSizeScale;
			this.PREFS.strokeSpacingScale = addThis.PREFS.strokeSpacingScale;
			this.zLoadOrder = addThis.zLoadOrder;
			this.housekeep();
			this.lastIdx = this.USR_EXPR.length - 1;
			this.idx = this.lastIdx;
			this.triggerUpdate = true;
			this.loadedSavedSession = true;
			this.showSessionHighlights = addThis.
			showSessionHighlights;
		};
	}

	//------------------------------------------------------------------------------
	var jClone = function(obj) {
		return JSON.parse(JSON.stringify(obj));
	}

	var sum = function(arr) {
		return arr.reduce(function(a, b) {
			return a + b
		});
	}

	var seq = function(b) {
		return Array.apply(null, Array(b)).map(function(_, c) {
			return c;
		});
	}

	var elementsWithValues = function(arr) {
		try {
			var reverseIndices = [],
				e = arr.length,
				r = 0;

			while (e--) {
				if (arr[e] !== null && arr[e] != undefined &&
					Object.keys(arr[e]).length !== 0)
					reverseIndices[r++] = e;
			}
			return reverseIndices;
		} catch (err) {
			console.error(err);
		}
	}

	// removeEmpty(arr,{ii}) - ii is an optional array of indices of arr to keep
	var removeEmpty = function(arr, ii) { //***Note: the expected indices should be reverse order (last->first)
		//bc of the optimized negative while loop
		try {
			var reverseIndices = [],
				arrnew = [],
				e = arr.length,
				r = 0;
			if (typeof ii === 'undefined') {
				while (e--) {
					if (arr[e] !== null && arr[e] != undefined &&
						arr[e].length !== undefined && Object.keys(arr[e])
						.length !== 0)
						reverseIndices[r++] = e;
				}
			} else {
				reverseIndices = ii;
				r = ii.length;
			}

			while (r--) {
				arrnew[r] = arr[reverseIndices[r]];
			}
			return arrnew;
		} catch (err) {
			console.error(err);
		}
	}

	/*////////////////////////////////////////////////////////////////////////////*/
	//
	//  TODO: Organize code...convert some functions to methods and prototypes
	//
	function RSelHighlights(RSel) {
		function updatePrefsFromPanel() {
			rsh.PREFS.strokeColor[rsh.idx] = document.getElementById("selRSHColors").value
			rsh.PREFS.strokeOpacity[rsh.idx] = document.getElementById("numRSHOpacity")
				.valueAsNumber
			rsh.PREFS.strokeDashstyle[rsh.idx] = document.getElementById("selRSHDash").selectedIndex;
			rsh.PREFS.strokeLinecap[rsh.idx] = document.getElementById("selRSHCap").selectedIndex;
			rsh.PREFS.strokeWidthScale[rsh.idx] = document.getElementById(
				"numRSHScaleWidth").valueAsNumber
			rsh.PREFS.strokeDashSizeScale[rsh.idx] = document.getElementById(
				"numRSHDashSizeScale").valueAsNumber
			rsh.PREFS.strokeSpacingScale[rsh.idx] = document.getElementById(
				"numRSHSpacingScale").valueAsNumber

			//Update variable with settings from the UI
			/*var jj = rsh.presets.linePropObjKeys.length;
			while (jj--) {
				lineProp = rsh.presets.linePropObjKeys[jj];
				eval(
					'rsh.PREFS[lineProp][rsh.idx] = document.getElementById(rsh.presets.linePropIds[jj]).' +
					rsh.presets.linePropAttrType[jj]);
			}*/
		}

		var updateHighlightButton = function() {
			// -----------------------------------------------------------------
			var hexToRgb = function(hex) {
				var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
				return result ? {
					r: parseInt(result[1], 16),
					g: parseInt(result[2], 16),
					b: parseInt(result[3], 16)
				} : null;
			};
			var rgbToHex = function(rgb) {
				rgb = rgb.match(
					/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i
				);
				return (rgb && rgb.length === 4) ? "#" +
					("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) +
					("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) +
					("0" + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';
			};

			// -----------------------------------------------------------------
			// Recolor background of Highlight button
			var hex = rsh.PREFS.strokeColor[rsh.idx],
				rgb;

			//$("#selRSHColors").val(hex).change();
			$("#selRSHColors").css('background-color', hex);

			rgb = hexToRgb(hex);
			$("#selRSHColors").css('border-bottom', 'solid 2px rgb(' +
				parseInt(rgb.r * 0.7) + ',' + parseInt(rgb.g * 0.7) + ',' + parseInt(
					rgb.b *
					0.7) + ')');
			if (!$("#selRSHColors").hasClass('rsh-btn-color')) $("#selRSHColors").addClass(
				'rsh-btn-color');

			//-----
			document.getElementById("selRSHColors").onclick = function() {
				document.getElementById("selRSHColors").onchange = function() {
					rsh.triggerUpdate = true;
					updatePrefsFromPanel();
					updateHighlightButton();
					updateExprMenu();
				};
			};
		}

		var updateDashButtons = function() {
			// Check to enable or disable line option input boxes
			var dashSizeScaleObj = document.getElementById("numRSHDashSizeScale"),
				strokeSpacingScaleObj = document.getElementById("numRSHSpacingScale");

			if (rsh.PREFS.strokeDashstyle[rsh.idx] !== 0) {
				dashSizeScaleObj.disabled = false;
				strokeSpacingScaleObj.disabled = false;
				dashSizeScaleObj.style.backgroundColor = "#FFFFFF";
				strokeSpacingScaleObj.style.backgroundColor = "#FFFFFF";
			} else {
				dashSizeScaleObj.disabled = true;
				strokeSpacingScaleObj.disabled = true;
				dashSizeScaleObj.style.backgroundColor = "#E0E0E0";
				strokeSpacingScaleObj.style.backgroundColor = "#E0E0E0";
			}
		}

		function updateEraseButton(disableBtn) {
			if (disableBtn === undefined) {
				(rsh.lastIdx === 0) ? disableBtn = true: disableBtn = !rsh.showSessionHighlights;
			}
			if (disableBtn) {
				rsh.eraseOnScreen_idx = true;
				$("a#btnRSHClearTop").addClass('disabled');
			} else {
				rsh.eraseOnScreen_idx = false; // reset
				$("a#btnRSHClearTop").removeClass('disabled');
			}
		}

		function updateLayerCountButtons() {
			if (rsh.lastIdx !== 0) {
				$("a#btnRSHSubtract").removeClass('disabled');
				$("a#btnRSHClearAll").removeClass('disabled')
				updateEraseButton(false);
			} else {
				$("a#btnRSHSubtract").addClass('disabled');
				$("a#btnRSHClearAll").addClass('disabled');
				updateEraseButton(true);
			}

			$("#txtRSHCount").html(rsh.idx + 1);
		}

		function updatePanelFromPrefs() {
			//console.log('updatePanelFromPrefs()')

			// Apply panel settings from saved preferences
			//$("#selRSHColors").val(rsh.PREFS.strokeColor[rsh.idx]).change();
			$("#numRSHOpacity").val(rsh.PREFS.strokeOpacity[rsh.idx]).change();
			$("#selRSHDash").val(rsh.presets.lineAttributes.strokeDashstyle[
				rsh.PREFS.strokeDashstyle[rsh.idx]]).change();
			$("#selRSHCap").val(rsh.presets.lineAttributes.strokeLinecap[
				rsh.PREFS.strokeLinecap[rsh.idx]]).change();
			$("#numRSHScaleWidth").val(rsh.PREFS.strokeWidthScale[rsh.idx]).change();
			$("#numRSHDashSizeScale").val(rsh.PREFS.strokeDashSizeScale[rsh.idx]).change();
			$("#numRSHSpacingScale").val(rsh.PREFS.strokeSpacingScale[rsh.idx]).change();

			updateHighlightButton();
			updateDashButtons();
			updateLayerCountButtons();
		}

		function updateExprMenu() {
			//console.log('updateExprMenu()')

			// Dropup menu for selecting layers
			var e = rsh.USR_EXPR.length,
				htmlText = '',
				selStatus;
			while (e--) {
				(e === rsh.idx) ? selStatus = 'active ': selStatus = '';
				if (rsh.USR_EXPR[e] === undefined) {
					exprText = '';
				} else {
					exprText = RSel.getExpressionText(rsh.USR_EXPR[e]);
				}
				/*					'<table class="rsh-expr-menu"><tr><td style="width: 27px">' +
									'<div class="icon-sign-blank fa fa-sign-blank rsh-expr-color" style="color:' +
									rsh.PREFS.strokeColor[e] + '"></div></td>' +
									'<td><a id="liRSHexpr" name="' + e + '" ' +
									'class = "rsh-expr-menu ' + selStatus +
									'" ' +
									'href="javascript:void(0)">' +
									exprText + '</a></td></tr></table></li>'*/
				htmlText += '<li>' +
					'<a id="liRSHexpr" name="' + e + '" ' +
					'class = "rsh-expr-menu ' + selStatus +
					'" ' + 'href="javascript:void(0)">' +
					'<div class="rsh-btn-row">' +
					'<div class="icon-sign-blank fa fa-sign-blank rsh-expr-color" style="color:' +
					rsh.PREFS.strokeColor[e] + '"></div>' +
					exprText + ' </div> ' +
					'</a></li>'
				if (e !== 0) htmlText +=
					'<li role="separator" class="divider rsh-expr-menu" style="background-color: #CBE1E8; color: #CBE1E8; margin: 0; padding: 0; height: 2px;"></li>';
			}
			updatePanelFromPrefs();
			$(".rsh-expr-menu").html(htmlText);

			$("a.rsh-expr-menu").click(function() {
				rsh.idx = parseInt(this.name);
				//console.log('click')
				//updatePanelFromPrefs();
				updateExprMenu();
				updateHighlightButton();
			});
		}
		// ---------------------------------------------------------------------
		var setupLineAttributes = function() {
				var bgRoadColor = document.getElementById("selRSHBgRoadColor").value,
					numHighlights = rsh.lastIdx + 1;

				updatePrefsFromPanel();

				// preset line attributes for each zoom level
				var z = 11;
				while (z--) { // for each zoom level
					rsh_seg[z] = {
						hlLineStyle: [],
						hlLineStyleOvrlp: [],
						bgLineStyle: {}
					};

					var strokeWidthAtZoom = rsh.presets.lineAttributes.strokeWidth[z],
						bgStrokeWidthAtZoom = strokeWidthAtZoom - parseInt(strokeWidthAtZoom *
							0.5),
						mm = numHighlights;

					while (mm--) {
						// Stroke width (line thickness)
						var separationAmt = parseInt((strokeWidthAtZoom) / numHighlights),
							strokeWidthScale = rsh.PREFS.strokeWidthScale[mm],
							strokeWidthAtZoomScaledOvrlp = Math.round(((strokeWidthAtZoom + 2) - mm *
								separationAmt) * strokeWidthScale),
							strokeWidthAtZoomScaled = Math.round(strokeWidthAtZoom *
								strokeWidthScale),
							strokeOpacity = rsh.PREFS.strokeOpacity[mm],
							strokeOpacityOvrlp = strokeOpacity;

						// Dash style and customized spacing
						var dashSpecs = rsh.presets.lineAttributes.strokeDasharray[
								rsh.PREFS.strokeDashstyle[mm]],
							dashSizeScale = rsh.PREFS.strokeDashSizeScale[mm],
							spacingScale = rsh.PREFS.strokeSpacingScale[mm],
							dashSize = dashSizeScale * strokeWidthAtZoomScaled,
							spacing = spacingScale * strokeWidthAtZoomScaled,
							dashSizeOvrlp = dashSizeScale * strokeWidthAtZoomScaledOvrlp,
							spacingOvrlp = spacingScale * strokeWidthAtZoomScaledOvrlp,
							strokeDashstyle, strokeDashstyleOvrlp;

						// Make width 1 if the strokeWidth is less than 1 at this zoom
						// Also increase opacity to compensate for the small strokeWidth
						if (strokeWidthAtZoomScaled <= 1) {
							strokeWidthAtZoomScaled = 1;
							if (strokeOpacity < 0.9) strokeOpacity = 0.9;
						}

						// adjust strokeWidth for multiple highlights
						strokeOpacityOvrlp = rsh.PREFS.strokeOpacity[mm];
						if (strokeWidthAtZoomScaledOvrlp <= 1) {
							strokeWidthAtZoomScaledOvrlp = 1;
							if (strokeOpacityOvrlp < 0.9) strokeOpacityOvrlp = 0.9;
						}

						switch (rsh.PREFS.strokeDashstyle[mm]) {
							case 0:
								strokeDashstyle = dashSpecs[0];
								strokeDashstyleOvrlp = dashSpecs[0];
								break;
							case 1:
								strokeDashstyle = dashSpecs[0] * dashSize + ' ' + dashSpecs[1] *
									spacing;
								strokeDashstyleOvrlp = dashSpecs[0] * dashSizeOvrlp + ' ' + dashSpecs[
										1] *
									spacingOvrlp;
								break;
							case 2:
								strokeDashstyle = dashSpecs[0] * dashSize + ' ' + dashSpecs[1] *
									spacing +
									' ' + dashSpecs[2] * strokeWidthAtZoomScaled + ' ' + dashSpecs[3] *
									spacing;
								strokeDashstyleOvrlp = dashSpecs[0] * dashSizeOvrlp + ' ' + dashSpecs[
										1] *
									spacingOvrlp + ' ' + dashSpecs[2] * strokeWidthAtZoomScaledOvrlp +
									' ' +
									dashSpecs[3] * spacingOvrlp;
								break;
						}

						rsh_seg[z].hlLineStyle[mm] = {
							strokeColor: rsh.PREFS.strokeColor[mm],
							strokeOpacity: strokeOpacity,
							strokeDashstyle: strokeDashstyle,
							strokeLinecap: rsh.presets.lineAttributes.strokeLinecap[rsh.PREFS.strokeLinecap[
								mm]],
							strokeWidth: strokeWidthAtZoomScaled,
							graphicZIndex: mm + 1
						};

						rsh_seg[z].hlLineStyleOvrlp[mm] = {
							strokeColor: rsh_seg[z].hlLineStyle[mm].strokeColor,
							strokeOpacity: strokeOpacityOvrlp,
							strokeDashstyle: strokeDashstyleOvrlp,
							strokeLinecap: rsh_seg[z].hlLineStyle[mm].strokeLinecap,
							strokeWidth: strokeWidthAtZoomScaledOvrlp,
							graphicZIndex: mm + 1
						};
					}

					// lineStyle for background (Bg) highlighting
					if (bgStrokeWidthAtZoom < 1) bgStrokeWidthAtZoom = 1;

					rsh_seg[z].bgLineStyle = {
						strokeColor: bgRoadColor,
						strokeOpacity: 0.9,
						strokeWidth: bgStrokeWidthAtZoom,
						strokeLinecap: "butt",
						strokeDashstyle: "solid",
						graphicZIndex: 0
					};
				}

				rsh.triggerUpdate = false;
			}
			// ---------------------------------------------------------------------
		function drawHighlightsLayer() {
			currExpr = RSel.getCurrentExpression();
			if (currExpr !== null) {
				rsh.USR_EXPR[rsh.idx] = currExpr;
			}
			setTimeout(updateExprMenu, 0);
			setTimeout(Highlight, 0);
			rsh.showSessionHighlights = true;
			rsh_oLayer.setVisibility(true);
			updateEraseButton(false);

			if (sessionStorage) sessionStorage.WME_RSHighlights = JSON.stringify(rsh);
		}

		function eraseHighlight() {
			rsh.USR_EXPR[rsh.idx] = {};

			updateEraseButton(true);
			setTimeout(updateExprMenu, 0);
			setTimeout(Highlight, 0);
		}

		function clearAllHighlights() {
			rsh_oLayer.destroyFeatures();
			rsh = new RSH();

			updatePanelFromPrefs(); //reset panel settings with defaults from rsh.PREFS
			updateExprMenu(); //reset expressions menu
			setupLineAttributes(); //reset line attributes in rsh.PREFS
			/*
			// quick fix for resetting RSel's select button due to bug
			document.getElementById("btnRSSelect").disabled = false;
			document.getElementById("btnRSSelect").style.background = '#E9E9E9';
			*/
			if (sessionStorage) sessionStorage.WME_RSHighlights = false;
		}

		// ---------------------------------------------------------------------
		function addHighlightLayer() {
			drawHighlightsLayer();
			currExpr = RSel.getCurrentExpression();
			if (Object.keys(rsh.USR_EXPR[rsh.idx]).length === 0 && currExpr === null) {
				return false;
			} else {
				rsh.addLayer();
				updatePanelFromPrefs();
				updateExprMenu();

				if (sessionStorage) sessionStorage.WME_RSHighlights = JSON.stringify(rsh);
				return true;
			}
		}

		function subtractHighlightLayer() {
			if (rsh.lastIdx > 0) {
				rsh.removeLayer();
				updatePanelFromPrefs();
				updateExprMenu();

				if (sessionStorage) sessionStorage.WME_RSHighlights = JSON.stringify(rsh);
				return true;
			} else {
				return false;
			}
		}

		// -----------------------------------------------------------------------------
		// -----------------------------------------------------------------------------
		function Highlight() {
			var doBgHighlighting = function(nodes, backgroundLine) {
				var lineFeature = new OL.Feature.Vector(new OL.Geometry.LineString(
						nodes),
					null, backgroundLine);
				rsh_oLayer.addFeatures([lineFeature]);
			};

			var doHighlighting = function(nodes, highlightLine, n) {
				if (n === 1) highlightLine.strokeOpacity = 0.9;

				var lineFeature = new OL.Feature.Vector(new OL.Geometry.LineString(
						nodes),
					null, highlightLine);
				rsh_oLayer.addFeatures([lineFeature]);
			};

			if (rsh.triggerUpdate) setupLineAttributes();
			if (rsh_oLayer.visibility) {

				var usrBgHighlight = document.getElementById("cbRSHBgHighlight").checked,
					usrEditable = document.getElementById("cbRSEditable").checked,
					usrSuppressRoad = document.getElementById("cbRSHSuppRoad").checked,
					currentZoom = Waze.map.zoom,
					numHighlights = rsh.lastIdx + 1,
					exprArrays = rsh.USR_EXPR,
					hlLineStyle = jClone(rsh_seg[currentZoom].hlLineStyle),
					hlLineStyleOvrlp = rsh_seg[currentZoom].hlLineStyleOvrlp,
					bgLineStyle = rsh_seg[currentZoom].bgLineStyle,
					wazeSegObjs = Waze.model.segments.objects,
					segIds = Object.keys(wazeSegObjs),
					numSegments = segIds.length;
				//subsegIds;

				// Redraw the highlights each time... there's got to be a better way to do this.
				rsh_oLayer.destroyFeatures();

				//var drawSegments = function(segIds) {
				var s = segIds.length,
					seg, segNodes, countHighlighted, h;
				while (s--) {
					//seg = wazeSegObjs.get(segIds[s]),
					seg = wazeSegObjs[segIds[s]];
					segNodes = seg.geometry.components;
					countHighlighted = 0;
					//setTimeout(, 0);
					for (h = 0; h < numHighlights; h++) {
						if (seg.arePropertiesEditable() || !usrEditable) {
							if (RSel.checkSegment(exprArrays[h], seg)) {
								countHighlighted++;
								if (countHighlighted == 1) {
									doHighlighting(segNodes, hlLineStyle[h],
										countHighlighted * usrSuppressRoad);
								} else {
									doHighlighting(segNodes, hlLineStyleOvrlp[h]);
								}
							}
						}
					}

					if (usrBgHighlight && countHighlighted === 0) {
						doBgHighlighting(segNodes, bgLineStyle);
					}
				}
				//};

				/*if (numSegments > 500) {
					var segStart = 0,
						segEnd = 300;
					while (segStart < numSegments) {
						if (segEnd < numSegments) {
							subsegIds = segmentIds.slice(segStart, segEnd);
						} else {
							subsegIds = segmentIds.slice(segStart);
						}
						setTimeout(function() {
							drawSegments(subsegIds)
						}, 0);
						segStart = segStart + segEnd;
						segEnd = segEnd * 2;
					}
				} else {
					setTimeout(function() {
						drawSegments(segmentIds)
					}, 0);
				}
				*/
			} else {
				rsh.showSessionHighlights = false;
			}
		}
		// -----------------------------------------------------------------------------
		// -----------------------------------------------------------------------------
		var InitRSelHighlights = function() { // return rsh

			var checkForAutosave = function() { // return rsh
				rsh = new RSH();

				// Load autosaved parameters from sessionStorage
				if (sessionStorage.WME_RSHighlights !== undefined) {
					rsh.importLayers(JSON.parse(sessionStorage.WME_RSHighlights));
					console.log("WMERSH: Imported data from previous session.")
						//var rsh = JSON.parse(sessionStorage.WME_RSHighlights);
				}

				if (rsh.USR_EXPR === undefined) {
					rsh = new RSH();
					currExpr = RSel.getCurrentExpression();
					if (currExpr === null) currExpr = {};
					rsh.USR_EXPR = [currExpr];
				}
				if (rsh.loadedSavedSession) {
					if (rsh.USR_EXPR[rsh.lastIdx] !== null) {
						rsh.addLayer();
					}
				}
				if (rsh.showSessionHighlights === undefined) {
					rsh.showSessionHighlights = false;
				}

				return rsh;
			};

			var ToggleDropBarMenu = function() {
				var dropBarPanel = document.getElementById("divRSHdropBarPanel"),
					dropBarHeader = document.getElementById("spanRSHdropBarHeader"),
					dropBarContents = document.getElementById("spanRSHdropBarContents"),
					mainPanel = document.getElementById("divRSHmainPanel");

				if (document.getElementById("aRSHdropBarLink").name == "Hide") {
					mainPanel.style.borderBottomLeftRadius = "5px";
					mainPanel.style.borderBottomRightRadius = "5px";
					dropBarPanel.style.borderWidth = 0;
					dropBarPanel.style.backgroundColor = "transparent";
					dropBarHeader.style.backgroundColor = "transparent";
					dropBarHeader.style.bottomBorderWidth = 0;
					dropBarHeader.innerHTML =
						'<a id="aRSHdropBarLink" name="Show" class="rsh-link" href="javascript:void(0);"><div class="rsh-arrow-up rsh-vcentered"></div> &nbsp;Show additional line options</a>';
					dropBarContents.style.display = "none";
				} else {
					mainPanel.style.borderBottomLeftRadius = 0;
					mainPanel.style.borderBottomRightRadius = 0;
					dropBarPanel.style.borderWidth = "2px";
					dropBarPanel.style.backgroundColor = "#F9F9F9";
					dropBarHeader.style.backgroundColor = "#D1D3D4";
					dropBarHeader.style.bottomBorderWidth = "1px";
					dropBarHeader.innerHTML =
						'<a id="aRSHdropBarLink" name="Hide" class="rsh-link" href="javascript:void(0);"><div class="rsh-arrow-down rsh-vcentered"></div> &nbsp;Hide additional line options</a>';
					dropBarContents.style.display = "block";
				}

				$("a#aRSHdropBarLink").click(ToggleDropBarMenu); //
			};

			var SetupRSHInterface = function() {
				// CSS for RSel Highlights
				var rshCSS = document.createElement("style");
				rshCSS.type = "text/css";
				// UI panel
				rshCSS.innerHTML =
					'.rsh-main-panel { width: 100%; background-color: #B1D4DF; ' +
					'					border-style: solid; border-width: 2px; border-collapse: collapse; ' +
					'					padding: 5px; border-color: #82B8C9; margin-top: 25px; ' +
					'					border-top-left-radius: 5px; border-top-right-radius: 5px; ' +
					'					border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; ' +
					'					vertical-align: middle}' +
					'.rsh-dropbar-panel { color: #444444; width: 100%; border-style: inset; margin-top: 0; ' +
					'					border-width: 2px; border-color: #D5D5D5; background-color: #f9f9f9; ' +
					'					border-bottom-left-radius: 5px; border-bottom-right-radius: 5px} ' + // border-bottom-left-radius: 5px; border-bottom-right-radius: 5px0
					'.rsh-dropbar-panel table { border: 0; width: 100%; display: inline-table;} ' +
					'.rsh-dropbar-panel td { font-size: 7pt; font-weight: 600; line-height: 6px; ' +
					'					padding: 0px 4px 4px 4px; margin: 0; vertical-align: middle; }' +
					'.rsh-arrow-up { width: 0; height: 0; border-left: 6px solid transparent; ' +
					'					border-right: 6px solid transparent; border-bottom: 6px solid black; }' +
					'.rsh-arrow-down { width: 0; height: 0; border-left: 6px solid transparent; ' +
					'					border-right: 6px solid transparent; border-top: 6px solid black; }' +
					'span.rsh-vcentered { position: relative; padding: 3px 5px 2px; width: 100%; display: inline-block; align: left; vertical-align: middle; text-align: left; }' +
					'div.rsh-vcentered { position: relative; top: 50%; left: 8px; transform: translate(-50%, -50%); display: inline-block; }' +
					'span.rsh-dropbar-contents {display: block; margin: 8px 0px 0px; padding: 0; border: 0 }' +
					'select.rsh-dropbar-panel { height:20px; width: 100px; background-color: #FFFFFF; border: 1px solid #959595; margin: 0px 0px 2px 2px; padding: 0; }' +
					'input.rsh-dropbar-panel { height:20px; width: 36px; background-color: #FFFFFF; border: 1px solid #C1C1C1; border-radius: 0; margin: 0px 0px 2px 2px; padding: 0; }' +
					'.rsh-dropbar-header { color: #000000; border-bottom: 1px solid #c0c0c0 }' +
					'a.rsh-link { text-decoration: none; }';


				//'input.rsh-dropdown-menu { width:45px; height:25px; margin:0px 5px; border: 1px solid #B4B4B4 }' +

				// Buttons
				rshCSS.innerHTML +=
					'.rsh-btn-row { position: relative; display: block; vertical-align: middle; ' +
					'				margin-top: 2px; margin-bottom: 2px; padding: 0}' +
					'.rsh-btn-container { height: 25px; position: relative; display: inline-table; ' +
					'				vertical-align: middle; padding: 0}' +
					'.rsh-btn-drop { height: 22px; background-color: #FEFEFE; border: 1px solid #CBE1E8; ' +
					'				margin-top: 0px; margin-bottom: 0px !important}' +
					'.rsh-btn-color { -moz-border-top-right-radius: 0px; -webkit-border-top-right-radius: 0px; ' +
					'				-moz-border-bottom-right-radius: 0px; -webkit-border-bottom-right-radius: 0px; ' +
					' 				-webkit-appearance: none; -moz-appearance: none; background-color: yellow; ' +
					'				width: 25px; height: 25px; padding: 7px 7px 7px 3px; ' +
					'				background-position: 50% 55%; background-repeat: no-repeat; ' +
					'				background-image:url(data:image/png;base64,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);' +
					'				!important }' +
					'.rsh-btn-highlight { -moz-border-top-left-radius: 0px; -webkit-border-top-left-radius: 0px; ' +
					'				-moz-border-bottom-left-radius: 0px; -webkit-border-bottom-left-radius: 0px; padding-right: 6px;}' + //border-bottom: solid 2px #E4E4E4; border-top: 0; border-right: 0; border-left: 0; }' +
					'.rsh-btn-counter, a.rsh-btn-counter:hover, a.rsh-btn-counter:active, a.rsh-btn-counter.active, a.rsh-btn-counter:focus { ' +
					'				height: 25px; width: 25px; padding: 4px; cursor: default; ' +
					'				background-color: #CFE0E6; border-left: solid 1px #7AB0BE; border-bottom: 0; ' +
					'				-moz-box-shadow: 	inset 0 4px 8px -3px #88888E, inset 0 -3px 8px -4px #88888E; ' +
					'				-webkit-box-shadow: inset 0 4px 8px -3px #88888E, inset 0 -3px 8px -4px #88888E; ' +
					'				box-shadow: 		inset 0 4px 8px -3px #88888E, inset 0 -3px 8px -4px #88888E; } ' +
					'.rsh-btn { font-size: 10 pt; font-weight: bold; color: #396179 }' +
					'.rsh-btn:hover { background-color: #B1DCE9; color: #48758C  }' +
					'.rsh-btn:disabled { color: #8FB5C5 }' +
					'.rsh-btn:active, .rsh-btn:focus, .rsh-btn.active { background-image: none; outline: 0; -webkit-box-shadow: none; box-shadow: none; } ' +
					'.rsh-btn-fa { color: #48758C; padding: 3px; height: 25px; !important}' +
					'.rsh-btn > .tooltip-inner { font-weight: bold }';

				rshCSS.innerHTML += // Dropup expressions menu
					'.rsh-expr-menu { font-size: 10px; max-height: 400px; width: 310px; overflow-x: hidden; overflow-y: auto;} ' +
					'li.rsh-expr-menu { background-color: #CBE1E8; color: #CBE1E8; margin: 0; padding: 0; height: 2px; !important } ' +
					'.rsh-expr-menu>li>a:active, .rsh-expr-menu>li>a.active { background-color: #D8E7EC } ' +
					'.rsh-expr-menu>li>a { word-wrap: break-word; white-space: normal; ' +
					'					padding-top: 8px; padding-bottom: 8px; padding-left:10px; padding-right:10px;} ' +
					'.rsh-expr-color { display: inline-block; margin-left: 0px; margin-right: 8px; padding: 0;}' +
					'';
				//					'.rsh-expr-menu>td>a:active, .rsh-expr-menu>li>a.active { background-color: #D8E7EC } ' +
				//					'.rsh-expr-menu>td>a { text-decoration: none; word-wrap: break-word; white-space: normal; ' +
				//					'					padding-top: 8px; padding-bottom: 8px; padding-left:10px; padding-right:10px;} ' +
				//					'.rsh-expr-menu table {border: 0; padding:0; margin:0; vertical-align: middle; display: inline-block; text-align: left;}' +

				// CSS-generated icons
				rshCSS.innerHTML +=
					'.rsh-icn-outerbox { display: inline-block; width: 13px; height: 11px; ' +
					'					margin: 5px 3px 6px 3px; padding: 0; font-size: 0; border: 0; ' +
					'					vertical-align: middle; line-height: 0px}' +
					'.rsh-icn-indark { display: inline-block; margin: 0; padding: 0; background-color: #555555 }' +
					'.rsh-icn-inwhite { display: inline-block; margin: 0; padding: 0; background-color: #FFFFFF }' +
					'.rsh-icn-inclear { display: inline-block; margin: 0; padding: 0; background-color: transparent }' +
					'.rsh-icn-brdark { display: block; margin: 0; padding: 0; background-color: #555555 }' +
					'.rsh-icn-brclear { display: block; margin: 0; padding: 0; background-color: transparent }' +
					'.rsh-icn-ingray { display: inline-block; margin: 0; padding: 0; background-color: #bbbbbb }' +
					'.rsh-icn-2x2 { width: 2px; height: 2px }' +
					'.rsh-icn-3x2 { width: 3px; height: 2px }' +
					'.rsh-icn-2x1 { width: 2px; height: 1px }' +
					'.rsh-icn-1x2 { width: 1px; height: 2px }' +
					'.rsh-icn-1x1 { width: 1px; height: 1px }' +
					'.rsh-icn-3x3 { width: 3px; height: 3px }' +
					'.rsh-icn-4x3 { width: 4px; height: 3px }' +
					'.rsh-icn-13x1 { width: 13px; height: 1px }';

				document.body.appendChild(rshCSS);

				//------------------------------------------------------------------
				// CSS-HTML Icons
				// 		[weight, opacity, stroke type, end cap, dash size, dash spacing]
				var rshIcons = [
					'<div class="rsh-icn-outerbox"><div class="rsh-icn-brdark" style="width: 13px; height: 3px"></div><div class="rsh-icn-brclear" style="width: 13px; height: 2px"></div><div class="rsh-icn-brdark" style="width: 13px; height: 2px"></div><div class="rsh-icn-brclear" style="width: 13px; height: 3px"></div><div class="rsh-icn-brdark" style="width: 13px; height: 1px"></div></div>',
					'<div class="rsh-icn-outerbox"><div class="rsh-icn-indark rsh-icn-3x3"></div><div class="rsh-icn-inwhite rsh-icn-3x3"></div><div class="rsh-icn-indark rsh-icn-3x3" style="background-color: #ababab !important"></div><div class="rsh-icn-inwhite rsh-icn-3x3" style="background-color: #f1f1f1 !important"></div><div class="rsh-icn-inwhite rsh-icn-3x3"></div><div class="rsh-icn-indark rsh-icn-3x3" style="background-color: #999999 !important"></div><div class="rsh-icn-inwhite rsh-icn-3x3" style="background-color: #f1f1f1 !important"></div><div class="rsh-icn-indark rsh-icn-3x3" style="background-color: #CCCCCC !important"></div><div class="rsh-icn-indark rsh-icn-3x3" style="background-color: #888888 !important"></div><div class="rsh-icn-inwhite rsh-icn-3x3" style="background-color: #fafafa !important"></div><div class="rsh-icn-indark rsh-icn-3x3" style="background-color: #bbbbbb !important"></div><div class="rsh-icn-inclear rsh-icn-3x3"></div><div class="rsh-icn-inwhite rsh-icn-3x3" style="background-color: #fbfbfB !important"></div><div class="rsh-icn-indark rsh-icn-3x3" style="background-color: #bbbbbb !important"></div><div class="rsh-icn-inclear rsh-icn-3x3"></div><div class="rsh-icn-indark rsh-icn-3x3" style="background-color: #dddddd !important"></div></div>',
					'<div class="rsh-icn-outerbox"><div class="rsh-icn-brdark" style="width: 13px; height: 2px"></div><div class="rsh-icn-brclear" style="width: 13px; height: 2px"></div><div class="rsh-icn-indark" style="width: 6px; height: 2px"></div><div class="rsh-icn-inclear" style="width: 1px; height: 2px"></div><div class="rsh-icn-indark" style="width: 6px; height: 2px"></div><div class="rsh-icn-brclear" style="width: 13px; height: 2px"></div><div class="rsh-icn-indark" style="width: 3px; height: 3px"></div><div class="rsh-icn-inclear" style="width: 2px; height: 3px"></div><div class="rsh-icn-indark" style="width: 3px; height: 3px"></div><div class="rsh-icn-inclear" style="width: 2px; height: 3px"></div><div class="rsh-icn-indark" style="width: 3px; height: 3px"></div></div>',
					'',
					'<div class="rsh-icn-outerbox" style="width: 13px !important"><div class="rsh-icn-indark rsh-icn-1x2"></div><div class="rsh-icn-inclear rsh-icn-2x2"></div><div class="rsh-icn-indark rsh-icn-1x2"></div><div class="rsh-icn-inclear rsh-icn-2x2"></div><div class="rsh-icn-indark rsh-icn-1x2"></div><div class="rsh-icn-inclear rsh-icn-2x2"></div><div class="rsh-icn-indark rsh-icn-1x2"></div><div class="rsh-icn-inclear rsh-icn-2x2"></div><div class="rsh-icn-indark rsh-icn-1x2"></div><!-->>--><div class="rsh-icn-brclear" style="width: 13px; height: 3px"></div><!--<<--><div class="rsh-icn-indark rsh-icn-3x2"></div><div class="rsh-icn-inclear rsh-icn-2x2"></div><div class="rsh-icn-indark rsh-icn-3x2"></div><div class="rsh-icn-inclear rsh-icn-2x2"></div><div class="rsh-icn-indark rsh-icn-3x2"></div><!-->>--><div class="rsh-icn-brclear" style="width: 13px; height: 2px"></div><!--<<--><div class="rsh-icn-indark" style="width: 6px; height: 2px"></div><div class="rsh-icn-inclear" style="width: 2px; height: 2px"></div><div class="rsh-icn-indark" style="width: 5px; height: 2px"></div></div>',
					'<div class="rsh-icn-outerbox"><div class="rsh-icn-brclear" style="width: 13px; height: 1px"></div><div class="rsh-icn-indark rsh-icn-4x3"></div><div class="rsh-icn-inclear" style="width: 5px; height: 3px"></div><div class="rsh-icn-indark rsh-icn-4x3"></div><div class="rsh-icn-brclear" style="width: 13px; height: 3px"></div><div class="rsh-icn-inclear rsh-icn-2x1"></div><div class="rsh-icn-ingray rsh-icn-1x1"></div><div class="rsh-icn-inclear" style="width: 7px; height: 1px"></div><div class="rsh-icn-ingray rsh-icn-1x1"></div><div class="rsh-icn-inclear rsh-icn-2x1"></div><div class="rsh-icn-inclear rsh-icn-2x1"></div><div class="rsh-icn-ingray" style="width: 9px; height: 1px"></div><div class="rsh-icn-inclear rsh-icn-2x1"></div><div class="rsh-icn-inclear rsh-icn-2x1"></div><div class="rsh-icn-ingray rsh-icn-1x1"></div><div class="rsh-icn-inclear" style="width: 7px; height: 1px"></div><div class="rsh-icn-ingray rsh-icn-1x1"></div><div class="rsh-icn-inclear rsh-icn-2x1"></div><div class="rsh-icn-brclear" style="width: 13px; height: 2px"></div></div>'
				];
				//------------------------------------------------------------------
				// Main RSel Highlights panel
				$("#RSselection").append(
					'<div class="rsh-main-panel" id="divRSHmainPanel" style="padding-bottom: 5px;">' +
					'<div class="rsh-btn-row">' +
					'	<div class="rsh-btn-container">' +
					'		<input type="checkbox" id="cbRSHSuppRoad" class="btn btn-default" ' +
					'				style="margin-bottom: 4px; margin-right: 0px; margin-left: 2px;" ' + // Suppress roads
					'			   data-toggle="tooltip" title="Suppress appearance of roads underneath highlights">' +
					'	</div>' +
					'	<div class="rsh-btn-container" style="margin-left: 2px;">' +
					'		<input type="color" id="selRSHColors" value="#FFFF00" class="btn rsh-btn-color" ' + // highlight color
					'			   	list="colors" style="" ' +
					'			   	data-toggle="tooltip" title="Highlight color">' +
					'			<datalist id="colors">' +
					'				<option>#CCFF66</option><option>#28F311</option><option>#66FFCC</option><option>#00F2F2</option><option>#0037D1</option>' +
					'				<option>#FFFF00</option><option>#F5950E</option><option>#E1201B</option><option>#FF00FF</option><option>#8000FF</option>' +
					'			</datalist>' +
					'		<button id="btnRSHighlight" class="btn btn-primary rsh-btn-fa rsh-btn rsh-btn-highlight active" ' + // HIGHLIGHT
					'				style="height:25px; width: 75px; margin-right: 3px; padding-right: 5px;" ' +
					'				data-toggle="tooltip" title="Update highlights">' +
					'				Highlight</button>' +
					'		<a id="btnRSHClearTop" class="btn btn-primary rsh-btn-fa rsh-btn disabled" href="javascript:void(0)">' + // undo eraser
					'		<div style="padding-left: 5px; padding-right: 5px" class="icon-eraser fa fa-eraser"' +
					'			data-toggle="tooltip" title="Erase active highlights"></div></a>' +
					'	</div>' +
					'	<div class="rsh-btn-container" style="margin-left: 5px">' +
					'		<div class="btn-group" role="group">' +
					'			<a id="btnRSHAdd" style="border-right: solid 1px #7AB0BE;" ' + // Add layer
					'				class="btn btn-primary rsh-btn-fa rsh-btn" href="javascript:void(0)">' +
					'				<div style="padding-left: 6px; padding-right: 5px" class="icon-plus fa fa-plus" ' +
					'				data-toggle="tooltip" title="Add new highlight layer"></div></a>' +
					'			<div class="btn-group dropup"  role="group">' +
					'				<a id="btnRSHCount" class="btn btn-primary rsh-btn-counter" ' + // layer counter
					'					href="javascript:void(0)" data-toggle="dropdown">' +
					'				<span id="txtRSHCount" style="width: 25px; color: #88888E; !important">0</span></a>' + // [#]
					'				<ul class="dropdown-menu pull-right dropdown-menu-right list-group rsh-expr-menu" ' +
					'					style="right: -75px; padding: 1px; border-radius: 2px; ">' +
					'					<li></li>' +
					'				</ul>' +
					'			</div>' +
					'			<a id="btnRSHSubtract" style="border-left: solid 1px #7AB0BE; border-right: solid 1px #7AB0BE;" ' + // Subtract layer
					'				class="btn btn-primary rsh-btn-fa rsh-btn disabled" href="javascript:void(0)">' +
					'				<div style="padding-left: 5px; padding-right: 5px" class="icon-minus fa fa-minus" ' +
					'				data-toggle="tooltip" title="Remove highlight layer"></div></a>' +
					'			<a id="btnRSHClearAll" style="border-left: solid 1px #B099B1;" ' + // Clear all
					'				class="btn btn-danger rsh-btn-fa disabled" href="javascript:void(0)">' +
					'				<div style="color: white; padding-left: 6px; padding-right: 7px !important" class="icon-trash fa fa-trash" ' +
					'					data-toggle="tooltip" title="Delete all"></div></a>' +
					'	</div></div>' +
					'</div>' +
					'<div class="rsh-btn-row" style="margin-top: 6px; margin-bottom: 0px;">' +
					'   <div class="rsh-btn-container" style="">' +
					'		<select class="btn rsh-btn-drop disabled" style="width: 166px; margin-left: 4px; margin-right: 2px">' +
					'			<option><i>(coming soon)</i></option>' +
					'		</select>' +
					'		<a id="btnRSHsave" class="btn rsh-btn-fa rsh-btn disabled" style="margin-bottom: 1px" href="javascript:void(0)">' + // Save
					'		<div style="font-size: 20px; padding:0; margin-right: 6px;" class="icon-save fa fa-save"' +
					'			data-toggle="tooltip" title="Save highlights"></div></a>' +
					'	</div>' +
					'	<div class="rsh-btn-container">' +
					'		<div class="rsh-btn-container">' +
					'			<input type="color" id="selRSHBgRoadColor" value="#c0c0c0" class="btn rsh-btn-drop" ' + // trace color
					' 			   	   list="bgRdcolors" style="width: 40px; height: 20px; margin-top:2px; margin-right: 4px; padding: 4px 8px 4px 8px; background-color: #CBE1E8;" ' +
					'			   	   data-toggle="tooltip" title="Tracing color for non-highlighted roads">' +
					'				<datalist id="bgRdcolors">' +
					'					<option>#FFFFFF</option><option>#c0c0c0</option><option>#000000</option><option>#264B01</option><option>#634D41</option>' +
					'				</datalist>' +
					'			<input type="checkbox" id="cbRSHBgHighlight"  class="btn btn-default" style="margin-bottom: 5px" ' + // Trace background roads checkbox
					'				   data-toggle="tooltip" title="Trace over non-highlighted roads with a single color">' +
					'		</div>' +
					'	</div>' +
					'</div>');

				$("[data-toggle=tooltip]").tooltip({
					placement: 'auto top',
					delay: {
						show: 1100,
						hide: 100
					},
					html: true,
					template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="my-tooltip-header"><b></b></div><div class="my-tooltip-body tooltip-inner" style="font-weight: bold; !important"></div></div>'
				});

				// Additional line options drop menu
				$("#RSselection").append(
					'<div id="divRSHdropBarPanel" class="rsh-dropbar-panel">' +
					'<span id="spanRSHdropBarHeader" class="rsh-vcentered">' +
					'<a id="aRSHdropBarLink" name="Hide" class="rsh-link" href="javascript:void(0);"><div class="rsh-arrow-up rsh-vcentered"></div> &nbsp;Hide additional line options</a></span>' +
					'<span id="spanRSHdropBarContents" class="rsh-dropbar-contents">' +
					// table for additional line options within dropBar panel
					'<table class="rsh-dropbar-panel">' +
					'<tr><td style="vertical-align: bottom">Weight</td><td style="vertical-align: bottom">Opacity</td>' +
					'<td rowspan=4>' +
					// inner table for dash style options
					'<table class="rsh-dropbar-panel" style="padding-left: 3px; padding-right: 3px; border-left: 1px solid #DFDFDF !important">' +
					'<tr><td colspan=2 style="vertical-align: bottom">Dash style</td>' +
					'</tr><tr><td colspan=2>' + rshIcons[2] +
					'<select id="selRSHDash" class="rsh-dropbar-panel" style="border-color: #B4B4B4"></select></td>' +
					'</tr><tr>' +
					'<td style="vertical-align: bottom">Dash width</td><td style="vertical-align: bottom">Spacing</td>' +
					'</tr><tr>' +
					'<td>' + rshIcons[4] +
					'<input type="number" id="numRSHDashSizeScale" style="border-radius: 3px; background-color: #EFEFEF;" class="rsh-dropbar-panel" min="0.1" max="10" value="1" step="0.1"  title="Dash size (relative)" disabled \></td>' +
					'<td>' + rshIcons[5] +
					'<input type="number" id="numRSHSpacingScale" style="border-radius: 3px; background-color: #EFEFEF;" class="rsh-dropbar-panel" min="0.1" max="10" value="1" step="0.1" title="Dash spacing (relative)" disabled \></td>' +
					'</tr></table></td>' +
					'</tr><tr>' +
					'<td>' + rshIcons[0] +
					'<input type="number" id="numRSHScaleWidth" style="border-radius: 3px" class="rsh-dropbar-panel" min="0.1" max="10" value="1" step="0.1" title="Line weight (relative)"></td>' +
					'<td>' + rshIcons[1] +
					'<input type="number" id="numRSHOpacity" style="border-radius: 3px" class="rsh-dropbar-panel" min="0" max="1" value="0.5" step="0.1" title="Color opacity (0–1)"></td>' +
					'</tr><tr>' +
					'<td colspan=2 style="vertical-align: bottom">End cap</td>' +
					'<tr><td colspan=2>' + '' +
					'<select id="selRSHCap" class="rsh-dropbar-panel" style="border-color: #B4B4B4"></select></td>' +
					'</tr></table>' +
					'</span></div>');
			};

			// Insert UI into side-panel of WME under RSel tab
			SetupRSHInterface();
			setTimeout(ToggleDropBarMenu, 0);

			// Retrieve any data from saved sessionStorage
			var rsh = checkForAutosave();

			// Setup event listeners/triggers

			$("#btnRSHighlight").click(drawHighlightsLayer);
			$("#btnRSHAdd").click(addHighlightLayer);
			$("#btnRSHSubtract").click(subtractHighlightLayer);
			$("#btnRSHClearTop").click(eraseHighlight);
			$("#btnRSHClearAll").click(clearAllHighlights);


			var d, c, dashSelection = document.getElementById("selRSHDash"),
				capSelection = document.getElementById("selRSHCap");

			for (d = 0; d < rsh.presets.lineAttributes.strokeDashstyle.length; d++) {
				dashSelection.add(new Option(rsh.presets.lineAttributes.strokeDashstyle[
					d]));
			};
			for (c = 0; c < rsh.presets.lineAttributes.strokeLinecap.length; c++) {
				capSelection.add(new Option(rsh.presets.lineAttributes.strokeLinecap[c]));
			};
			//$("#selRSHColors").click(
			document.getElementById("selRSHColors").onclick = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
				updateHighlightButton();
			};
			document.getElementById("cbRSHBgHighlight").onclick = function() {
				document.getElementById("numRSHSpacingScale").onchange = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
			};
		};
			dashSelection.onclick = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
				updateDashButtons();
			};
			capSelection.onclick = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
			};
			document.getElementById("numRSHOpacity").onclick = function() {
				document.getElementById("numRSHOpacity").onchange = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
			};
			};
			document.getElementById("numRSHScaleWidth").onclick = function() {
				document.getElementById("numRSHScaleWidth").onchange = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
			};
			};
			document.getElementById("numRSHDashSizeScale").onclick = function() {
				document.getElementById("numRSHDashSizeScale").onchange = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
			};
			};
			document.getElementById("numRSHSpacingScale").onclick = function() {
				document.getElementById("numRSHSpacingScale").onchange = function() {
				rsh.triggerUpdate = true;
				updatePrefsFromPanel();
			};
			};

			window.addEventListener("beforeunload", function() {
				//rsh.USR_EXPR[rsh.lastIdx] = {};
				rsh.housekeep();
				rsh.idx = rsh.lastIdx;
				if (sessionStorage) sessionStorage.WME_RSHighlights = JSON.stringify(
					rsh);
			}, false);

			// Event listeners to redraw highlights
			Waze.map.events.register("zoomend", Waze.map, function() {
				Highlight();
				setTimeout(Highlight, 1500)
			});
			Waze.map.events.register("moveend", Waze.map, function() {
				Highlight();
				setTimeout(Highlight, 1500)
			});
			Waze.map.events.register("mergeend", Waze.map, function() {
				Highlight();
				setTimeout(Highlight, 1500)
			});
			//Waze.map.events.register("move", Waze.map, Highlight);
			//Waze.map.events.register("changelayer", Waze.map, Highlight);
			//Waze.map.events.register("visibilitychanged", Waze.map, Highlight);

			return rsh
		}

		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		var createHighlightLayer = function() {
			var rsh_oLayer = new OL.Layer.Vector("Road Selector Highlights", {
				rendererOptions: {
					zIndexing: true
				},
				uniqueName: '__RSel_Highlights'
			});
			I18n.translations.en.layers.name["__RSel_Highlights"] =
				"Road Selector Highlights";
			rsh_oLayer.setZIndex(52);
			Waze.map.addLayer(rsh_oLayer);
			Waze.map.addControl(new OL.Control.DrawFeature(rsh_oLayer, OL.Handler
				.Path));
			rsh_oLayer.displayOutsideMapExtent = true;
			rsh_oLayer.setVisibility(rsh.showSessionHighlights);

			return rsh_oLayer;
		};
		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		var rsh = InitRSelHighlights();

		// Update panel
		if (rsh.loadedSavedSession) {
			updatePanelFromPrefs(rsh);
			updateExprMenu(rsh);
		} else {
			updatePanelFromPrefs(rsh);
		}
		var rsh_seg = [];

		// Add Road Selector Highlights layer to map
		var rsh_oLayer = createHighlightLayer();
		RSH_TEST = rsh;

		Highlight();
	}

	/*//////////////////////////////////////////////////////////////////////////*/
	function waitForWMERSel() {
		var waitCount = 0,
			tryInit = function() {
				try {
					if (typeof unsafeWindow.RoadSelector === 'undefined') {
						if (waitCount++ < 20) {
							setTimeout(tryInit, 600);
						} else {
							console.error(
								'WME RSel Highlights: Could not link up with WME Road Selector.');
						}
					} else {
						/* unsafeWindow.RoadSelector:
								checkSegment(expression, segment)
								getCurrentExpression()
								getExpressionText(expression)
								getSavedNames()
								getSavedExpression(name)
						*/
						var RSel = unsafeWindow.RoadSelector;
						RSelHighlights(RSel);
					}
				} catch (err) {
					console.error(err)
				}
			};
		tryInit();
	}

	setTimeout(waitForWMERSel, 500);
})();