WME Road Selector Highlights

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

目前為 2016-02-11 提交的版本,檢視 最新版本

// ==UserScript==
// @name            WME Road Selector Highlights
// @namespace       https://greasyfork.org/users/11629-TheLastTaterTot
// @version         0.5.1.4
// @description     Adds highlighting ability to WME Road Selector. Requires WME Road Selector to function.
// @author          TheLastTaterTot
// @include         https://editor-beta.waze.com/*editor/*
// @include         https://www.waze.com/*editor/*
// @exclude         https://www.waze.com/*user/editor/*
// ==/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 () {
            var numLayers = this.USR_EXPR.length,
                notEmptyIndexes = elementsWithValues(this.USR_EXPR),
                numNewLayers = notEmptyIndexes.length,
                p = this.presets.linePropObjKeys.length,
                lineProp;

            this.USR_EXPR = removeEmpty(this.USR_EXPR, notEmptyIndexes)

            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,
                exprText,
                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]);
                }

                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-square 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_OL.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_OL.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_OL.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_OL.addFeatures([lineFeature]);
            };

            if (rsh.triggerUpdate) setupLineAttributes();
            if (rsh_OL.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_OL.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: 285px; 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,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAGCAYAAAD37n+BAAAKqWlDQ1BJQ0MgUHJvZmlsZQAASImVlgdUU2kWx7/30hstEOmE3qQLBJBeQxGkgyiEJCShhJCCgl0ZHMGKiAiWER0FUXBUigwqIoptUGzYJ8igoI6DBRsq+4Al7O7ZOXv25tz3fuee++77f1/ed84fAPJtlkiUDasAkCOUiqOD/eiJScl0nBxAyA8AKwBYbInINyoqHPxtfLg72Qtu2UzM+vu+/xqqHK6EDQAUhXA6R8LOQfgkku1skVgKAEqE1I0XS0UTXI6wuhgRiHDtBPOmuH2C06f4xmRPbLQ/wn8AgCezWGIeAKQRpE7PZ/OQOWRktcBeyBEIEfZB2IvNZ3EQXoXw7Jyc3Ak+grBF+r/M4f3bzHTFTBaLp+CptUwGPkAgEWWzCv7P7fjfkZMtm36HEZJkvjgkemLNyJ7VZuWGKViYPi9ymgWcyf5J5stC4qaZLfFPnmYOKyBsmmVZcb7TzBLPPCuQMmOnWZwbrZjPlQTGKOZzmeEKDdnzFJwhCGJOcyE/NmGa8wXx86ZZkhUTNtPjr6iLZdEKzRniIMUacyQz2tisGQ1SfmzIjLZEhQYONyBQURfGKfpFUj/FTFF2lKKfmx2sqEvyYxTPSpEPbJozWaFRM3OiFPsDYkABEAI2iAAsIAFSwAViKXeJdEKwf66oQCzg8aV0X+TEcOlMIdt2Nt3R3sEFgInzN/X3vqNNniuIdmWmlusOAOMS8h1un6mlDwHQch8ALdJMzQQ5Q8qpADRvZMvE+VM19MQFA4hAGVGoBfSBMbAANsARuAAP4AMCQSiIBLEgCSxCVPNBDhCDxWAZWA2KQSnYAraDKrAX7Ae14Cg4DlpAOzgHLoKr4Aa4Ax4CORgEL8EI+ADGIAjCQRSICmlBBpApZA05QgzICwqEwqFoKAlKg3iQEJJBy6C1UClUBlVB+6A66BfoFHQOugz1QvehfmgYegt9gVEwGVaH9WAz2A5mwL5wGBwLL4R5cB5cCBfBm+BKuAY+AjfD5+Cr8B1YDr+ER1EARULRUIYoGxQD5Y+KRCWjMlBi1ApUCaoCVYNqQLWhulG3UHLUK9RnNBZNRdPRNmgPdAg6Ds1G56FXoDegq9C16GZ0F/oWuh89gv6OoWB0MdYYdwwTk4jhYRZjijEVmIOYJswFzB3MIOYDFoulYc2xrtgQbBI2E7sUuwG7G9uI7cD2YgewozgcTgtnjfPEReJYOCmuGLcTdwR3FncTN4j7hCfhDfCO+CB8Ml6IX4OvwB/Gn8HfxD/HjxFUCKYEd0IkgUMoIGwmHCC0Ea4TBgljRFWiOdGTGEvMJK4mVhIbiBeIj4jvSCSSEcmNNJ8kIK0iVZKOkS6R+kmfyWpkK7I/OYUsI28iHyJ3kO+T31EoFDOKDyWZIqVsotRRzlOeUD4pUZVslZhKHKWVStVKzUo3lV4rE5RNlX2VFykXKlcon1C+rvxKhaBipuKvwlJZoVKtckqlT2VUlarqoBqpmqO6QfWw6mXVITWcmplaoBpHrUhtv9p5tQEqimpM9aeyqWupB6gXqIPqWHVzdaZ6pnqp+lH1HvURDTWNORrxGks0qjVOa8hpKJoZjUnLpm2mHafdpX2ZpTfLdxZ31vpZDbNuzvqoqaPpo8nVLNFs1Lyj+UWLrhWolaW1VatF67E2WttKe772Yu092he0X+mo63josHVKdI7rPNCFda10o3WX6u7XvaY7qqevF6wn0tupd17vlT5N30c/U79c/4z+sAHVwMtAYFBucNbgBV2D7kvPplfSu+gjhrqGIYYyw32GPYZjRuZGcUZrjBqNHhsTjRnGGcblxp3GIyYGJhEmy0zqTR6YEkwZpnzTHabdph/NzM0SzNaZtZgNmWuaM80LzevNH1lQLLwt8ixqLG5bYi0ZllmWuy1vWMFWzlZ8q2qr69awtYu1wHq3de9szGy32cLZNbP7bMg2vjb5NvU2/bY023DbNbYttq/tTOyS7bbaddt9t3e2z7Y/YP/QQc0h1GGNQ5vDW0crR7ZjteNtJ4pTkNNKp1anN3Os53Dn7Jlzz5nqHOG8zrnT+ZuLq4vYpcFl2NXENc11l2sfQ50RxdjAuOSGcfNzW+nW7vbZ3cVd6n7c/S8PG48sj8MeQ3PN53LnHpg74GnkyfLc5yn3onulef3kJfc29GZ513g/9TH24fgc9Hnua+mb6XvE97WfvZ/Yr8nvo7+7/3L/jgBUQHBASUBPoFpgXGBV4JMgoyBeUH3QSLBz8NLgjhBMSFjI1pA+ph6TzaxjjoS6hi4P7Qojh8WEVYU9DbcKF4e3RcARoRHbIh7NM50nnNcSCSKZkdsiH0eZR+VF/TofOz9qfvX8Z9EO0cuiu2OoMakxh2M+xPrFbo59GGcRJ4vrjFeOT4mvi/+YEJBQliBPtEtcnng1STtJkNSajEuOTz6YPLogcMH2BYMpzinFKXcXmi9csvDyIu1F2YtOpyqnslJPpGHSEtIOp31lRbJqWKPpzPRd6SNsf/YO9kuOD6ecM8z15JZxn2d4ZpRlDPE8edt4w3xvfgX/lcBfUCV4kxmSuTfzY1Zk1qGs8eyE7MYcfE5azimhmjBL2JWrn7skt1dkLSoWyfPc87bnjYjDxAclkGShpFWqjhidazIL2Q+y/nyv/Or8T4vjF59YorpEuORagVXB+oLnhUGFPy9FL2Uv7VxmuGz1sv7lvsv3rYBWpK/oXGm8smjl4KrgVbWriauzVv+2xn5N2Zr3axPWthXpFa0qGvgh+If6YqVicXHfOo91e39E/yj4sWe90/qd67+XcEqulNqXVpR+3cDecGWjw8bKjeObMjb1bHbZvGcLdotwy92t3ltry1TLCssGtkVsay6nl5eUv9+euv1yxZyKvTuIO2Q75JXhla07TXZu2fm1il91p9qvunGX7q71uz7u5uy+ucdnT8Nevb2le7/8JPjp3r7gfc01ZjUV+7H78/c/OxB/oPtnxs91B7UPlh78dkh4SF4bXdtV51pXd1j38OZ6uF5WP3wk5ciNowFHWxtsGvY10hpLj4FjsmMvfkn75e7xsOOdJxgnGk6antzVRG0qaYaaC5pHWvgt8tak1t5Toac62zzamn61/fVQu2F79WmN05vPEM8UnRk/W3h2tEPU8eoc79xAZ2rnw/OJ5293ze/quRB24dLFoIvnu327z17yvNR+2f3yqSuMKy1XXa42X3O+1vSb829NPS49zdddr7fecLvR1ju398xN75vnbgXcunibefvqnXl3eu/G3b3Xl9Inv8e5N3Q/+/6bB/kPxh6ueoR5VPJY5XHFE90nNb9b/t4od5Gf7g/ov/Y05unDAfbAyz8kf3wdLHpGeVbx3OB53ZDjUPtw0PCNFwteDL4UvRx7Vfyn6p+7Xlu8PvmXz1/XRhJHBt+I34y/3fBO692h93Ped45GjT75kPNh7GPJJ61PtZ8Zn7u/JHx5Prb4K+5r5TfLb23fw74/Gs8ZHxexxKxJK4BCEs7IAODtIQAoSQBQEd9MVJryx5Mx5f+nCPwdT3noyUCcy/4OAGJXARCO3HcidzMklX0AmLBHsT4AdnJS5D9DkuHkODWLjLhMzKfx8Xd6AODaAPgmHh8f2z0+/u0AIhbxNh15U758IsJtEC9jEJHk6NT17Sn4z/gHnKUFv2j8BSIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAFZaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CkzCJ1kAAAA7SURBVBgZY2QAAlZW1v8gmhD4/fs3IyNMESFNIMUgtXANIA4uTTDFGBqwaUJWDJLHCkA24bINqwZ8ggBTiBIhAxjugwAAAABJRU5ErkJggg==);' +
                    '				!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; padding: 0;">' +
                    '			<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: 2px 4px 2px 4px; 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_OL = new OL.Layer.Vector("Road Selector Highlights", {
                rendererOptions: {
                    zIndexing: true
                },
                uniqueName: '__RSel_Highlights'
            });
            I18n.translations.en.layers.name["__RSel_Highlights"] =
                "Road Selector Highlights";
            rsh_OL.setZIndex(52);
            Waze.map.addLayer(rsh_OL);
            Waze.map.addControl(new OL.Control.DrawFeature(rsh_OL, OL.Handler
                .Path));
            rsh_OL.displayOutsideMapExtent = true;
            rsh_OL.setVisibility(rsh.showSessionHighlights);

            return rsh_OL;
        };
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        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_OL = createHighlightLayer();
        RSH_TEST = rsh;

        Highlight();
    }

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

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