您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Create custom highlighters to colorize segments according to your selection criteria. Requires WME Road Selector to function.
当前为
// ==UserScript== // @name WME Road Selector Highlights // @namespace https://greasyfork.org/users/11629-TheLastTaterTot // @version 0.7.7.10 // @description Create custom highlighters to colorize segments according to your selection criteria. 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/* // @require https://greasyfork.org/scripts/17641-rsel-exprparser-basic/code/rsel-exprparser-basic.js?version=113850 // @run-at document-end // ==/UserScript== //--------------------------- // Add option to turn off RSel Highlights (not just hide the layer) // // DEBUG: //RSelEP = RSelExprParser; //rsh = {}; //--------------------------- var RSel, rsh, rsh_seg, rsh_OL, rsh_OLu, isFirefox = /\bfirefox\b/i.test(navigator.userAgent), stylesVersion = 1, userBgHighlight, userEditableOnly, userSuppressRoad, zoomLevel; function RSH() { this._clone = function(obj) { if (!obj) { return JSON.parse(JSON.stringify(this)); } else { return JSON.parse(JSON.stringify(obj)); } }; this._seq = function(b) { return Array.apply(null, Array(b)).map(function(_, c) { return c; }); }; this._sum = function(arr) { return arr.reduce(function(a, b) { return a + b }); }; this.presets = { linePropObjKeys: [ 'strokeColor', 'strokeOpacity', 'strokeDashstyle', 'strokeLinecap', 'strokeWidthScale', 'strokeDashSizeScale', 'strokeGapScale', ], linePropIds: ['selRSHColors', 'numRSHOpacity', 'selRSHDash', 'selRSHCap', 'numRSHScaleWidth', 'numRSHDashSizeScale', 'numRSHGapScale' ], linePropAttrType: ['value', 'valueAsNumber', 'selectedIndex', 'selectedIndex', 'valueAsNumber', 'valueAsNumber', 'valueAsNumber' ], lineAttributes: { strokeWidth: [3, 5, 7, 8, 9, 10.4, 13, 14, 14, 14, 14], strokeDashstyle: ["solid", "- - - - -", "– ‧ – ‧"], strokeDasharray: [ ["solid"], [1, 1], [2, 1, 1, 1] ], strokeLinecap: ["butt", "round", "square"], underRoads: false }, lineStyles: { Default: { strokeColor: "#FFFF00", strokeOpacity: 0.5, strokeDashstyle: 0, strokeLinecap: 0, strokeWidthScale: 1, strokeDashSizeScale: 1, strokeGapScale: 1 }, "Small Dots": { strokeColor: "#FFFF00", "strokeOpacity": 0.8, "strokeDashstyle": 1, "strokeLinecap": 1, "strokeWidthScale": 0.4, "strokeDashSizeScale": 0.1, "strokeGapScale": 2.4 }, "Large Dots": { strokeColor: "#FFFF00", "strokeOpacity": 0.5, "strokeDashstyle": 1, "strokeLinecap": 1, "strokeWidthScale": 1.5, "strokeDashSizeScale": 0.01, "strokeGapScale": 1.5 }, "Fuzzy Lines": { strokeColor: "#FFFF00", "strokeOpacity": 0.8, "strokeDashstyle": 1, "strokeLinecap": 0, "strokeWidthScale": 1, "strokeDashSizeScale": 0.1, "strokeGapScale": 0.1 }, Ticks: { strokeColor: "#FFFF00", "strokeOpacity": 0.7, "strokeDashstyle": 1, "strokeLinecap": 0, "strokeWidthScale": 1, "strokeDashSizeScale": 0.2, "strokeGapScale": 0.3 } }, }; this.STYLES = this._clone(this.presets.lineStyles); this.getNewRuleObject = function() { return { expr: {}, text: null, isValidated: null, tryOnce: false }; }; this.getDefaultStyleObject = function() { return this._clone(this.STYLES.Default); }; this.HIGHLIGHTS = [{ rule: this.getNewRuleObject(), style: this.getDefaultStyleObject() }]; this.lastIdx = 0; this.idx = 0; this.rulesStack = [0]; this.prevZoom = null; this.doNotDrawRshIdx = true; this.triggerUpdate = true; this.showSessionHighlights = false; this.loadedSavedSession = false; this.stylesVersion = 1; this.highlighterName = undefined; this.clearStoredExpression = function(idx) { if (!idx) idx = this.idx; this.HIGHLIGHTS[idx].rule = this.getNewRuleObject() }; this.storeExprText = function(idx) { if (!idx) idx = this.idx; this.HIGHLIGHTS[idx].rule.text = RSel.getExpressionText(RSel.getCurrentExpression()); this.HIGHLIGHTS[idx].rule.isValidated = null; this.HIGHLIGHTS[idx].rule.tryOnce = false; }; this.storeCurrentExpression = function(idx) { var force = false; switch (true) { case idx: force = true; case !idx: idx = this.idx; break; } if (force || this.HIGHLIGHTS[idx].rule.isValidated === true || (this.HIGHLIGHTS[idx].rule.isValidated === null && !this.doNotDrawRshIdx)) { var currExpr = RSel.getCurrentExpression(); this.HIGHLIGHTS[idx].rule = { expr: (currExpr) ? currExpr : {}, text: (currExpr) ? RSel.getExpressionText(currExpr) : null, isValidated: this.HIGHLIGHTS[idx].rule.isValidated, tryOnce: false //note that once saved, tryOnce is always set to false... thus, if it is true, that means it failed the second time. }; } //else { console.debug('Expression parsing was invalid. Expression not stored.')} }; this.validateExprText = function(idx, clearExprBox) { if (!idx) idx = this.idx; document.getElementById('outRSExpr').style.display = 'none'; if (idx === this.idx) { //save whatever expression is active in RSel if (this.HIGHLIGHTS[this.idx].rule.isValidated === false) { if (this.HIGHLIGHTS[this.idx].rule.tryOnce === true && document.getElementById('rshExprWarn') === null) { this.storeCurrentExpression(true); //force save } //} else if (!this.HIGHLIGHTS[this.idx].rule.text) { // this.storeCurrentExpression(); } } if (rsh.HIGHLIGHTS[idx].rule.text) { //console.debug(rsh.HIGHLIGHTS[idx].rule.text); RSelExprParser.updateExpression(rsh.HIGHLIGHTS[idx].rule.text); var currExprText = document.getElementById('outRSExpr').value; if (currExprText === rsh.HIGHLIGHTS[idx].rule.text) { this.storeCurrentExpression(idx); // update the expr obj in case it needs to be this.HIGHLIGHTS[idx].rule.isValidated = true; //$('#outRSExpr').wrap('<div id="divRSExpr" style="padding: 4px; border: 1px solid transparent;"></div>'); //document.getElementById('outRSExpr').style.padding = '2px 0'; //document.getElementById('outRSExpr').style.border = '1px solid transparent'; //document.getElementById('outRSExpr').style.borderColor = '#8000FF'; } else { this.HIGHLIGHTS[idx].rule.isValidated = false; if (!clearExprBox) { document.getElementById('outRSExpr').innerHTML = '\ <div id="rshExprWarn" style="padding: 4px; background: repeating-linear-gradient(45deg, rgba(251,255,0,0.2), rgba(251,255,0,.2) 10px, rgba(255,224,0,0.4) 10px, rgba(255,224,0,0.4) 20px)">' + document.getElementById('outRSExpr').innerHTML + '</div>' + '<div style="font-size: 10px; color: crimson; font-weight: 600; outline: darkgray dotted 1px; margin-top: 5px; padding: 3px 5px;">' + '<i class="fa fa-exclamation-triangle"></i> <b>RSel Highlights</b> was unable to parse the saved expression text to exactly match the original expression.' + '<div style="font-size: 10px; font-weight: 600; margin-top: 4px;">' + 'Your original selection will be used for highlighting, but it cannot be edited – only deleted. ' + '<b>Please make a bug report <a href="https://www.waze.com/forum/posting.php?mode=reply&f=819&t=173107" target="_blank">here</a></b>.' + '</div></div>'; } } } // else nothing saved to validate if (clearExprBox) RSelExprParser.rselButtons.clear(); document.getElementById('outRSExpr').style.display = 'block'; return this.HIGHLIGHTS[idx].rule.isValidated; }; this.recreateExpressionText = function(idx) { if (!idx) idx = rsh.idx; var exprIsValidated = this.validateExprText(idx); if (exprIsValidated) { return this.HIGHLIGHTS[idx].rule.text; //} else if (exprIsValidated === null) { //empty // return RSel.getExpressionText(rsh.HIGHLIGHTS[idx].rule.expr); } else { this.HIGHLIGHTS[this.idx].rule.tryOnce = true; //try again if (this.validateExprText(idx)) { // -- this time alllowing expr save/update if no parse warning is present in DOM return this.HIGHLIGHTS[idx].rule.text; //new expression text } else if (this.HIGHLIGHTS[idx].rule.text) { //resort to using return RSel.getExpressionText(rsh.HIGHLIGHTS[idx].rule.expr); } else { return null; } } }; this.addHighlighter = function() { //RSelExprParser.rselButtons.clear(); this.idx = this.HIGHLIGHTS.length; //next index this.HIGHLIGHTS[this.idx] = { rule: this.getNewRuleObject(), style: this.getDefaultStyleObject() }; this.lastIdx = this.idx; this.rulesStack[this.idx] = this.idx; this.triggerUpdate = true; this.doNotDrawRshIdx = true; }; this.deleteHighlighter = function(idx) { if (idx === undefined) idx = this.idx; //RSelExprParser.rselButtons.clear(); this.HIGHLIGHTS.splice(idx, 1); if (this.idx === idx) { ((this.idx - 1) > 0) ? this.idx-- : this.idx = 0; } else if (this.idx > idx) { this.idx--; } this.lastIdx = this.HIGHLIGHTS.length - 1; this.rulesStack = this._seq(this.HIGHLIGHTS.length); this.triggerUpdate = true; }; this.importHighlights = function(addThis) { this.HIGHLIGHTS = addThis.HIGHLIGHTS; this.rulesStack = this._seq(this.HIGHLIGHTS.length); this.lastIdx = addThis.HIGHLIGHTS.length - 1; this.idx = this.lastIdx; this.triggerUpdate = true; this.loadedSavedSession = true; this.showSessionHighlights = !!addThis.showSessionHighlights; this.doNotDrawRshIdx = !!addThis.doNotDrawRshIdx; this.highlighterName = addThis.highlighterName; this.HIGHLIGHTS.forEach(function(a){a.rule.tryOnce = false;}) }; this.addStyles = function() { try { var rshStyles = JSON.parse(localStorage.RSHighlights_Styles); if (rshStyles.constructor === Object) { if (this.stylesVersion !== stylesVersion) { $.extend(true, rshStyles, this.presets.lineStyles); this.STYLES = rshStyles; this.stylesVersion = stylesVersion; localStorage.RSHighlights_Styles = JSON.stringify(this.STYLES); } else { this.STYLES = rshStyles; } } } catch (err) { /* ignore */ } }; this.getCurrentExprText = RSelExprParser.getCurrentExprText; } //------------------------------------------------------------------------------ /*////////////////////////////////////////////////////////////////////////////*/ // // TODO: Organize code...convert some functions to methods and prototypes // function RSelHighlights() { var updatePrefsFromPanel = function() { rsh.HIGHLIGHTS[rsh.idx].style.strokeColor = document.getElementById("selRSHColors").value; rsh.HIGHLIGHTS[rsh.idx].style.strokeOpacity = document.getElementById("numRSHOpacity").valueAsNumber; rsh.HIGHLIGHTS[rsh.idx].style.strokeDashstyle = document.getElementById("selRSHDash").selectedIndex; rsh.HIGHLIGHTS[rsh.idx].style.strokeLinecap = document.getElementById("selRSHCap").selectedIndex; rsh.HIGHLIGHTS[rsh.idx].style.strokeWidthScale = document.getElementById("numRSHScaleWidth").valueAsNumber; rsh.HIGHLIGHTS[rsh.idx].style.strokeDashSizeScale = document.getElementById("numRSHDashSizeScale").valueAsNumber; rsh.HIGHLIGHTS[rsh.idx].style.strokeGapScale = document.getElementById("numRSHGapScale").valueAsNumber; rsh.HIGHLIGHTS[rsh.idx].style.underRoads = document.getElementById('cbRSHunderRoads').checked; rsh.storeCurrentExpression(); }; var updateHighlighterColorButton = function() { // ----------------------------------------------------------------- // Recolor background of Highlight button //$("#selRSHColors").val(hex).change(); document.getElementById('selRSHColorBtn').style.backgroundColor = rsh.HIGHLIGHTS[rsh.idx].style.strokeColor; document.getElementById('selRSHColors').value = rsh.HIGHLIGHTS[rsh.idx].style.strokeColor; }; var updateDashButtons = function() { // Check to enable or disable line option input boxes var dashSizeScaleObj = document.getElementById("numRSHDashSizeScale"), strokeGapScaleObj = document.getElementById("numRSHGapScale"); if (rsh.HIGHLIGHTS[rsh.idx].style.strokeDashstyle !== 0) { dashSizeScaleObj.disabled = false; strokeGapScaleObj.disabled = false; dashSizeScaleObj.style.backgroundColor = "#FFF"; strokeGapScaleObj.style.backgroundColor = "#FFF"; } else { dashSizeScaleObj.disabled = true; strokeGapScaleObj.disabled = true; dashSizeScaleObj.style.backgroundColor = "#E0E0E0"; strokeGapScaleObj.style.backgroundColor = "#E0E0E0"; } }; var disableEraseButton = function(disableBtn) { if (disableBtn === undefined) { disableBtn = false; try { if (!rsh.showSessionHighlights || rsh.doNotDrawRshIdx) { disableBtn = true; } else if (rsh.HIGHLIGHTS[rsh.idx].rule.expr && Object.keys(rsh.HIGHLIGHTS[rsh.idx].rule.expr).length === 0) { disableBtn = true; } } catch (err) { disableBtn = true; } } if (disableBtn) { $("a#btnRSHClearTop").addClass('disabled'); } else { $("a#btnRSHClearTop").removeClass('disabled'); } }; var updateLayerCountButtons = function() { if (rsh.lastIdx !== 0) { $("a#btnRSHSubtract").removeClass('disabled'); //$("a#btnRSHClearAll").removeClass('disabled') //disableEraseButton(false); } else { $("a#btnRSHSubtract").addClass('disabled'); //$("a#btnRSHClearAll").addClass('disabled'); disableEraseButton(); } $("#txtRSHCount").html(rsh.idx + 1); }; var updatePanelFromPrefs = function() { //console.debug('updatePanelFromPrefs()') //rsh.storeCurrentExpression(); //update rsh obj with current expression // Apply panel settings from saved preferences //$("#selRSHColors").val(rsh.HIGHLIGHTS[rsh.idx].style.strokeColor).change(); $("#numRSHOpacity").val(rsh.HIGHLIGHTS[rsh.idx].style.strokeOpacity).change(); $("#selRSHDash").val(rsh.presets.lineAttributes.strokeDashstyle[rsh.HIGHLIGHTS[rsh.idx].style.strokeDashstyle]).change(); $("#selRSHCap").val(rsh.presets.lineAttributes.strokeLinecap[rsh.HIGHLIGHTS[rsh.idx].style.strokeLinecap]).change(); $("#numRSHScaleWidth").val(rsh.HIGHLIGHTS[rsh.idx].style.strokeWidthScale).change(); $("#numRSHDashSizeScale").val(rsh.HIGHLIGHTS[rsh.idx].style.strokeDashSizeScale).change(); $("#numRSHGapScale").val(rsh.HIGHLIGHTS[rsh.idx].style.strokeGapScale).change(); document.getElementById('cbRSHunderRoads').checked = rsh.HIGHLIGHTS[rsh.idx].style.underRoads; updateHighlighterColorButton(); updateDashButtons(); updateLayerCountButtons(); }; // --------------------------------------------------------------------- var setupLineStyles = function() { var bgRoadColor = document.getElementById("selRSHBgRoadColor").value, numHighlights = rsh.rulesStack.length; updatePrefsFromPanel(); // adjust some UI stuff for enhancing usability /* if (!!document.getElementById('selRSHighlighter').value && document.getElementById('selRSHighlighter').value !== '') { document.getElementById('saveRSHighlighter').style.color = "#ed503b"; }*/ // preset line attributes for each zoom level var z = 11; while (z--) { // for each zoom level rsh_seg[z] = { hlLineStyle: [], hlLineStyleOvrlp: [], bgLineStyle: {} }; var underRoadsScaler = .5, strokeWidthAtZoom = rsh.presets.lineAttributes.strokeWidth[z], bgStrokeWidthAtZoom = strokeWidthAtZoom - parseInt(strokeWidthAtZoom * 0.5), mm = numHighlights; while (mm--) { if (rsh.HIGHLIGHTS[mm].style.underRoads === undefined) rsh.HIGHLIGHTS[mm].style.underRoads = false; // Stroke width (line thickness) var separationAmt = parseInt((strokeWidthAtZoom) / numHighlights), strokeWidthScale = rsh.HIGHLIGHTS[mm].style.strokeWidthScale + (underRoadsScaler * rsh.HIGHLIGHTS[mm].style.underRoads), strokeWidthAtZoomScaledOvrlp = Math.round(((strokeWidthAtZoom + 2) - mm * separationAmt) * strokeWidthScale), strokeWidthAtZoomScaled = Math.round(strokeWidthAtZoom * strokeWidthScale), strokeOpacity = rsh.HIGHLIGHTS[mm].style.strokeOpacity, strokeOpacityOvrlp = strokeOpacity; // Dash style and customized gap size var dashSpecs = rsh.presets.lineAttributes.strokeDasharray[rsh.HIGHLIGHTS[mm].style.strokeDashstyle], dashSizeScale = rsh.HIGHLIGHTS[mm].style.strokeDashSizeScale, gapScale = rsh.HIGHLIGHTS[mm].style.strokeGapScale, dashSize = dashSizeScale * strokeWidthAtZoomScaled, gap = gapScale * strokeWidthAtZoomScaled, dashSizeOvrlp = dashSizeScale * strokeWidthAtZoomScaledOvrlp, gapOvrlp = gapScale * 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.HIGHLIGHTS[mm].style.strokeOpacity; if (strokeWidthAtZoomScaledOvrlp <= 1) { strokeWidthAtZoomScaledOvrlp = 1; if (strokeOpacityOvrlp < 0.9) strokeOpacityOvrlp = 0.9; } switch (rsh.HIGHLIGHTS[mm].style.strokeDashstyle) { case 0: strokeDashstyle = dashSpecs[0]; strokeDashstyleOvrlp = dashSpecs[0]; break; case 1: strokeDashstyle = dashSpecs[0] * dashSize + ' ' + dashSpecs[1] * gap; strokeDashstyleOvrlp = dashSpecs[0] * dashSizeOvrlp + ' ' + dashSpecs[1] * gapOvrlp; break; case 2: strokeDashstyle = dashSpecs[0] * dashSize + ' ' + dashSpecs[1] * gap + ' ' + dashSpecs[2] * strokeWidthAtZoomScaled + ' ' + dashSpecs[3] * gap; strokeDashstyleOvrlp = dashSpecs[0] * dashSizeOvrlp + ' ' + dashSpecs[1] * gapOvrlp + ' ' + dashSpecs[2] * strokeWidthAtZoomScaledOvrlp + ' ' + dashSpecs[3] * gapOvrlp; break; } rsh_seg[z].hlLineStyle[mm] = { strokeColor: rsh.HIGHLIGHTS[mm].style.strokeColor, strokeOpacity: strokeOpacity, strokeDashstyle: strokeDashstyle, strokeLinecap: rsh.presets.lineAttributes.strokeLinecap[rsh.HIGHLIGHTS[mm].style.strokeLinecap], 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; }; // --------------------------------------------------------------------- // Use this function if there is a chance that layers or highlighters may be set to off // ...this will reset them. If that is not necessary, call Highlights(optionalArg) directly instead. function drawHighlights(ev,optionalArg) { rsh.doNotDrawRshIdx = false; //console.debug(ev,optionalArg); if (optionalArg === 'import') { optionalArg = true; } else { var currExpr = RSel.getCurrentExpression(); if (currExpr !== null) { rsh.storeCurrentExpression(true); //force save } } requestAnimationFrame(updateExprMenu); if (rsh.HIGHLIGHTS.length) { if (optionalArg === undefined) { optionalArg = rsh.idx; } if (rsh.HIGHLIGHTS[rsh.idx].rule.expr && Object.keys(rsh.HIGHLIGHTS[rsh.idx].rule.expr).length === 0) { rsh.doNotDrawRshIdx = true; } else if (rsh.HIGHLIGHTS[rsh.idx].rule.isValidated === false && rsh.HIGHLIGHTS[rsh.idx].rule.tryOnce === true) { if (document.getElementById('rshExprWarn') === null) RSelExprParser.rselButtons.clear(); } rsh.showSessionHighlights = true; rsh_OL.setVisibility(true); rsh_OLu.setVisibility(true); disableEraseButton(false); //console.info(optionalArg); requestAnimationFrame(function() { Highlight(optionalArg); //if rsh.triggerUpdate, this will also be assoc with an expr save sessionStorage.RSHighlights = JSON.stringify(rsh); }); } } var eraseHighlight = function(idx) { if (idx === undefined) idx = rsh.idx; rsh.doNotDrawRshIdx = true; rsh.clearStoredExpression(); if (!rsh.HIGHLIGHTS[idx].style.underRoads) rsh_OL.destroyFeaturesByMapKey(idx); else rsh_OLu.destroyFeaturesByMapKey(idx); updateExprMenu(); requestAnimationFrame(Highlight); disableEraseButton(true); } function clearAllHighlights() { var confirmDelete = confirm("Are you sure you want to clear all highlighting rules?"); if (confirmDelete) { //RSelExprParser.rselButtons.clear(); rsh = new RSH(); rsh.triggerUpdate = true; rsh.doNotDrawRshIdx = true; rsh_OL.destroyAllFeatureMaps(); rsh_OLu.destroyAllFeatureMaps(); rsh_OL.setVisibility(false); rsh_OLu.setVisibility(false); updatePanelFromPrefs(); updateExprMenu(); //reset expressions menu -- also updates panel with settings from rsh.HIGHLIGHTS[].style setupLineStyles(); //reset line attributes saved in rsh.HIGHLIGHTS[].style sessionStorage.RSHighlights = null; } } // --------------------------------------------------------------------- function addNewHighlighter() { var currExpr = RSel.getCurrentExpression(), clearExpr = false; if (document.getElementById('rshExprWarn')) clearExpr = true; if (rsh.HIGHLIGHTS[rsh.idx].rule.text === null || rsh.HIGHLIGHTS[rsh.idx].rule.text.length === 0) { // add rule to current empty index and then adds a new index if (currExpr !== null) drawHighlights([],rsh.idx) rsh.addHighlighter(); rsh.doNotDrawRshIdx = true; updateExprMenu(); // -- also updates panel with settings from rsh.HIGHLIGHTS[].style sessionStorage.RSHighlights = JSON.stringify(rsh); if (clearExpr) RSelExprParser.rselButtons.clear(); } else { // adds an empty index and then stores the current selection rule into new index if currExpr is not empty rsh.addHighlighter(); rsh.doNotDrawRshIdx = true; //rsh.storeCurrentExpression(true); updateExprMenu(); // -- also updates panel with settings from rsh.HIGHLIGHTS[].style sessionStorage.RSHighlights = JSON.stringify(rsh); if (clearExpr) RSelExprParser.rselButtons.clear(); } } var deleteHighlighter = function(idx) { if (idx === undefined) idx = rsh.idx; if (rsh.lastIdx > 0) { //var confirmDelete = confirm("Are you sure you want to delete the selected highlighter?"); //if (confirmDelete) { if (!rsh.HIGHLIGHTS[idx].style.underRoads) rsh_OL.destroyFeaturesByMapKey(idx); else rsh_OLu.destroyFeaturesByMapKey(idx); rsh.deleteHighlighter(idx); //updatePanelFromPrefs(); updateExprMenu(true); // -- also updates panel with settings from rsh.HIGHLIGHTS[].style sessionStorage.RSHighlights = JSON.stringify(rsh); //} } else { eraseHighlight(idx); } }; var exportRSelHighlighter = function(ev, str) { var rsh_saved, highlighterName, htmlStr, msgStr, uniqueName, style; if (str === undefined) { rsh_saved = JSON.parse(localStorage.RSHighlights); highlighterName = document.getElementById("selRSHighlighter").value; exportStr = '{"' + highlighterName + '":' + JSON.stringify(rsh_saved[highlighterName]).replace(/("#[\w\d]{6})\s?/ig, '$1') + '}'; style = 'position: absolute; bottom: 110%; width: 100px; left: -44px;'; } else { var dateName = new Date(), d = 'Highlight ' + dateName.getFullYear()+ '-' + dateName.getMonth()+ '-' + dateName.getDate() + ' ' + dateName.getHours() + '.' + dateName.getMinutes() + '.' + dateName.getSeconds(); highlighterName = prompt('Please provide a unique name for the highlighter', d); if (highlighterName !== null) { exportStr = '{"' + highlighterName + '":{"HIGHLIGHTS":[' + JSON.stringify(rsh.HIGHLIGHTS[rsh.idx]).replace(/("#[\w\d]{6})\s?/ig, '$1') + ']}}'; rsh_saved = {}; rsh_saved[highlighterName] = true; style = 'position: absolute; width: 200px; bottom: 0; left: 20px;'; } } if (rsh_saved[highlighterName]) { htmlStr = '<div id="rshMenuNote" style="' + style + '">'; msgStr = '"' + highlighterName + '" was copied to clipboard'; copyToClipboard(exportStr); } else { htmlStr = '<div id="rshMenuNote" style="' + style + '">'; msgStr = "Select a highlighter to export"; document.getElementById('selRSHighlighter').focus(); } setTimeout(function() { $(ev.target).append(htmlStr + msgStr + '</div>') }, 300); setTimeout(function() { $('#rshMenuNote').remove() }, 5000); document.getElementById('rshMenuNote').onclick = function(e){e.stopPropagation(); this.remove()}; }; // --------------------------------------------------------------------- // --------------------------------------------------------------------- var findNodeIndex = function(attr, val) { for (var i = 0, iLength = this.length; i < iLength; i++) { if (this[i].querySelector('[' + attr + '="' + String(val) + '"]')) return i; } return null; }; var dragEndExpr = function(ev) { //$(this.children).each(function(i,node){node.style.opacity = 1;}); //console.debug('DragEnd:',ev.target) $('div.rsh-expr-item').css('opacity', ''); $('div.rsh-expr-item').prop('draggable', false); $('.rsh-expr-menu').css('pointer-events', 'auto'); $('.rsh-expr-menu').removeClass('rsh-dragging'); $('div.rsh-expr-item').removeClass('rsh-drag'); //$('.rsh-expr-dragicn').each(function(i,node){node.style.pointerEvents = 'auto';}); //$('div.rsh-expr-item').each(function(i,node){node.removeEventListener('dragstart',dragStartExpr,false)}); $('.rsh-expr-text').each(function(i, node) { node.removeEventListener('dragover', allowDropExpr, false) }); $('.rsh-expr-text').each(function(i, node) { node.removeEventListener('dragenter', dragEnterExpr, false) }); $('div.rsh-expr-item').each(function(i, node) { node.removeEventListener('drop', dropExpr, false) }); $('div.rsh-expr-item').each(function(i, node) { node.removeEventListener('dragend', dragEndExpr, false) }); window.removeEventListener('mouseup', dragEndExpr, false); }; var dropExpr = function(ev) { //console.debug('Drop:',this); if (ev.preventDefault()) ev.preventDefault(); //ev.stopPropagation(); var exprListEl = document.getElementsByClassName('rsh-expr-menu')[0], draggedExprID = ev.dataTransfer.getData('text'), draggedExprEl = document.querySelector('.rsh-expr-item[value="' + draggedExprID + '"]').parentNode, targetExprID = this.getAttribute('value'), refNode = exprListEl.childNodes[findNodeIndex.call(exprListEl.childNodes, 'value', targetExprID)]; exprListEl.insertBefore(draggedExprEl, refNode); exprListEl.insertBefore(refNode, draggedExprEl); //$('.rsh-expr-item:not([value="' + draggedExprID + '"]>.rsh-expr-text)').each(function(i,node){node.removeEventListener('drop',dropExpr,false)}); var draggedExprIdx = parseInt(draggedExprID), targetExprIdx = parseInt(targetExprID), shiftIdx = 0, rulesLength = rsh.rulesStack.length, rulesIndex = rsh._seq(rulesLength); if ((draggedExprIdx !== targetExprIdx) && (draggedExprIdx + 1 !== targetExprIdx)) { //console.debug('Expression inserted at position',targetExprIdx); var draggedExpr = rsh.HIGHLIGHTS[draggedExprIdx]; if (targetExprIdx !== 100) { if (draggedExprIdx > targetExprIdx) shiftIdx = 1; //moved to earlier pos in array rsh.HIGHLIGHTS.splice(targetExprIdx, 0, draggedExpr); //inert expression after target position rsh.HIGHLIGHTS.splice(draggedExprIdx + shiftIdx, 1); //remove expression from previous position rulesIndex.splice(targetExprIdx, 0, draggedExprIdx); rulesIndex.splice(draggedExprIdx + shiftIdx, 1); rsh.rulesStack.splice(targetExprIdx, 0, draggedExprIdx); rsh.rulesStack.splice(draggedExprIdx + shiftIdx, 1); } else { rsh.HIGHLIGHTS.push(draggedExpr); rsh.HIGHLIGHTS.splice(draggedExprIdx, 1); //remove expression from previous position rulesIndex.push(draggedExprIdx); rulesIndex.splice(draggedExprIdx, 1); rsh.rulesStack.push(draggedExprIdx); rsh.rulesStack.splice(draggedExprIdx, 1); } rsh.idx = rulesIndex.findIndex(function(r) { if (r === rsh.idx) return true }); rsh.rulesStack = rsh.rulesStack.map(function(a, i) { return (a[i] !== false) ? i : false }); document.getElementById('btnRSHmenu').click(); //fix this... why separate eventlisteners? document.getElementById('btnRSHCount').click(); //fix this... why separate eventlisteners? rsh.triggerUpdate = true; requestAnimationFrame(function() { Highlight(true) }); } else { console.debug('Not inserted after expression', targetExprID); } }; var allowDropExpr = function(ev) { if (ev.preventDefault()) ev.preventDefault(); ev.dataTransfer.dropEffect = 'move'; return false; }; var dragEnterExpr = function(ev) { //console.debug('DragEnter:',ev.target); $('div.rsh-expr-item:not([value="' + this.parentNode.value + '"])').removeClass('rsh-drag'); this.parentNode.classList.add('rsh-drag'); }; var dragStartExpr = function(ev) { //console.debug('DragStart:',this); var draggedExprID = this.getAttribute('value'); //console.debug('Dragging expression',parseInt(draggedExprID)+1); ev.dataTransfer.setData('text', draggedExprID); $('div.rsh-expr-item:not([value="' + draggedExprID + '"]>.rsh-expr-text)').each(function(i, node) { node.addEventListener('drop', dropExpr, false) }); $('.rsh-expr-menu').css('pointer-events', 'none'); $('.rsh-expr-menu').addClass('rsh-dragging'); //$('.rsh-expr-dragicn').each(function(i,node){node.style.pointerEvents = 'none';}); //$(this.children).each(function(i,node){node.style.opacity = 0.2;}); this.style.opacity = 0.4; }; function updateExprMenu(doExpr) { //console.debug('updateExprMenu()') // Dropup menu for selecting layers var e = rsh.HIGHLIGHTS.length, htmlText = '<li class="rsh-hidden"><div value="100" class="rsh-expr-item"><span class="rsh-expr-text"></span></div></li>', exprText, selStatus; while (e-- > 0) { if (e === rsh.idx) { selStatus = ' active'; if (doExpr) exprText = rsh.recreateExpressionText(e); else exprText = RSel.getExpressionText(rsh.HIGHLIGHTS[e].rule.expr); } else { selStatus = ''; exprText = RSel.getExpressionText(rsh.HIGHLIGHTS[e].rule.expr); } if (!exprText) { exprText = ' '; //if (e === rsh.idx) RSelExprParser.rselButtons.clear(); } else { exprText += '<i title="Export highlighter" class="rsh-expr-export fa fa-share-square-o fa-fw"></i>'; } htmlText += '<li><div value="' + e + '" ' + 'class = "rsh-expr-item' + selStatus + '">' + '<span class="rsh-expr-del fa fa-times"></span><span class="fa fa-square rsh-expr-color fa-pull-left" style="color:' + rsh.HIGHLIGHTS[e].style.strokeColor + '"></span>' + '<span class="rsh-expr-text">' + exprText + '<span class="rsh-expr-spacer"></span></span>' + '<span class="rsh-expr-dragicn"><i class="fa fa-ellipsis-v" style="padding: 1px;"></i><i class="fa fa-ellipsis-v"></i></span>' + '</div></li>'; } $(".rsh-expr-menu").html(htmlText); requestAnimationFrame(updatePanelFromPrefs); //------ if (rsh.HIGHLIGHTS[rsh.idx].rule.expr && Object.keys(rsh.HIGHLIGHTS[rsh.idx].rule.expr).length === 0) rsh.doNotDrawRshIdx = true; else rsh.doNotDrawRshIdx = false; $('div.rsh-expr-item').click(function(e) { if (rsh.triggerUpdate) Highlight(); //do not make async rsh.idx = parseInt(this.getAttribute('value')); //console.debug(this); console.debug(e); updateExprMenu(true); //updates expression in RSel as well as saves it in a highlighter slot disableEraseButton(); if (!rsh.HIGHLIGHTS[rsh.idx].rule.text) RSelExprParser.rselButtons.clear(); }); $('.rsh-expr-export').click(function(e) { e.stopPropagation(); exportRSelHighlighter(e, true); }); $('.rsh-expr-del').click(function(e) { e.stopPropagation(); //console.debug(this); var idx = parseInt(this.parentNode.getAttribute('value')); deleteHighlighter(idx) document.getElementById('btnRSHCount').click(); }); $('div.rsh-expr-item').each(function(i, node) { node.addEventListener('dragstart', dragStartExpr, false) }); $('.rsh-expr-dragicn').each(function(i, node) { node.addEventListener('mousedown', function(e) { //console.debug('hi'); e.stopPropagation(); $('div.rsh-expr-item').prop('draggable', true); $('.rsh-expr-text').each(function(i, node) { node.addEventListener('dragover', allowDropExpr, false) }); $('.rsh-expr-text').each(function(i, node) { node.addEventListener('dragenter', dragEnterExpr, false) }); $('div.rsh-expr-item').each(function(i, node) { node.addEventListener('dragend', dragEndExpr, false) }); window.addEventListener('mouseup', dragEndExpr, false); }, false) }); } // ----------------------------------------------------------------------------- var toggleDropBarMenu = function(hide) { var dropbarPanel = document.getElementById("divRSHdropbarPanel"), dropbarHeader = document.getElementById("divRSHdropbarHeader"), dropbarContents = document.getElementById("divRSHdropbarContents"), mainPanel = document.getElementById("divRSHmainPanel"); if (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; document.getElementById('aRSHdropbarLinkShow').style.display = 'inline-block'; document.getElementById('aRSHdropbarLinkHide').style.display = 'none'; $('div.rsh-styles-menu').css('display', 'none'); dropbarContents.style.display = "none"; } else { mainPanel.style.borderBottomLeftRadius = 0; mainPanel.style.borderBottomRightRadius = 0; dropbarPanel.style.borderWidth = "2px"; //dropbarPanel.style.backgroundColor = "#F9F9F9"; dropbarHeader.style.backgroundColor = "#DAEBF1"; dropbarHeader.style.bottomBorderWidth = "1px"; document.getElementById('aRSHdropbarLinkShow').style.display = 'none'; document.getElementById('aRSHdropbarLinkHide').style.display = 'inline-block'; $('div.rsh-styles-menu').css('display', 'inline-block'); dropbarContents.style.display = "block"; } }; // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- function Highlight(optionalArg) { var checkAllSegs = false, redrawFeats = false, recheckRules = false, //Note: for both recheck and redraw, remove the featureMapping... lineStylesUpdate = rsh.triggerUpdate; if (rsh_OL.visibility) { if (lineStylesUpdate) { setupLineStyles(); //rsh_OL.destroyFeaturesByMapKey(rsh.idx); //checkAllSegs = true; } // Pass Boolean true or rsh.idx. TODO: Allow specifying rsh.idx var currentZoom = Waze.map.zoom, hlLineStyle = rsh._clone(rsh_seg[currentZoom].hlLineStyle), hlLineStyleOvrlp = rsh_seg[currentZoom].hlLineStyleOvrlp, bgLineStyle = rsh_seg[currentZoom].bgLineStyle, segments = Waze.model.segments.objects, //*********** ids = Object.keys(segments), highlighterRuleIndexes, newFeatureArray = [], newFeatureArrayu = [], newBgFeatureArray = [], delFeatureArray = [], delFeatureArrayu = [], delBgFeat, seg, highlightIsTrue, countHighlighted, redraw, featureExists; if (rsh.doNotDrawRshIdx) { if (userSuppressRoad) optionalArg = true; //must redraw because highlight style with this setting enabled might change depending on which highlight rules are active highlighterRuleIndexes = rsh.rulesStack.filter(function(a) { return a !== rsh.idx }); rsh_OL.destroyFeaturesByMapKey(rsh.id); rsh_OLu.destroyFeaturesByMapKey(rsh.idx); } else { highlighterRuleIndexes = rsh.rulesStack; } if (!userBgHighlight) requestAnimationFrame(function() { rsh_OL.destroyFeaturesByMapKey('bg') }); if (optionalArg === true) { //rechecks and redraws everything rsh_OL.destroyAllFeatureMaps(); rsh_OLu.destroyAllFeatureMaps(); //console.debug('Reset requested: rsh_OL.destroyAllFeatureMaps();') } else { if (zoomLevel !== currentZoom) { redrawFeats = highlighterRuleIndexes.map(function(a) {return a}); rsh_OL.destroyFeatures(); rsh_OLu.destroyFeatures(); } if (optionalArg === null) { //checks all available segs – not just the ones on-screen, but does not recheck rules nor redraws checkAllSegs = true; } else if (optionalArg === 'd') { //does not recheck rules, but redraws all already existing highlights redrawFeats = highlighterRuleIndexes.map(function(a) {return a}); // (e.g., for line style changes, changes to seg geometry, ZOOM CHANGES) rsh_OL.destroyFeatures(); //---destroy all features, but don't remove featureMapping rsh_OLu.destroyFeatures(); } else if (optionalArg === 'a') { //recheck all the rules, but does not redraw if no assignment change recheckRules = true; // (e.g., for rule changes or changes to seg properties other than geometry & style) checkAllSegs = true; } else if (optionalArg && optionalArg.constructor === Number && rsh.rulesStack.contains(optionalArg)) { //recheck and redraw all for specified rsh.idx //if (lineStylesUpdate) { if (!redrawFeats) redrawFeats = highlighterRuleIndexes.map(function() { return optionalArg }); checkAllSegs = true; //} else { rsh_OL.destroyFeaturesByMapKey(optionalArg); rsh_OLu.destroyFeaturesByMapKey(optionalArg); if (userBgHighlight) rsh_OL.destroyFeaturesByMapKey('bg'); //} } else if (lineStylesUpdate) { if (!redrawFeats) redrawFeats = highlighterRuleIndexes.map(function() { return rsh.idx }); checkAllSegs = true; rsh_OL.destroyFeaturesByMapKey(rsh.idx); rsh_OLu.destroyFeaturesByMapKey(rsh.idx); if (userBgHighlight) rsh_OL.destroyFeaturesByMapKey('bg'); } } if (!redrawFeats) redrawFeats = highlighterRuleIndexes.map(function() { return false }); //Define separate objects to store features for each highlight rule rsh.rulesStack.map(function(h) { if (rsh_OL._featureMap[String(h)] === undefined) rsh_OL._featureMap[String(h)] = {} if (rsh_OLu._featureMap[String(h)] === undefined) rsh_OLu._featureMap[String(h)] = {} }); var s = ids.length, h, H; while (s--) { if (checkAllSegs || rsh_OL.isOnScreen(ids[s])) { //only care about onscreen segments seg = segments[ids[s]]; //segments.get(ids[s]), countHighlighted = 0; for (h of highlighterRuleIndexes) { if (Object.keys(rsh.HIGHLIGHTS[h].rule.expr).length === 0) { rsh_OL.destroyFeaturesByMapKey(h); rsh_OLu.destroyFeaturesByMapKey(h); redraw = redrawFeats.shift(); break; } redraw = redrawFeats.shift(); H = String(h); if (!rsh.HIGHLIGHTS[h].style.underRoads) { if (rsh_OL._featureMap[H][ids[s]]) { highlightIsTrue = rsh_OL._featureMap[H][ids[s]].highlight; //featureExists = (rsh_OL._featureMap[H][ids[s]].feature) ? true : false; featureExists = (rsh_OL._featureMap[H][ids[s]].feature && rsh_OL._featureMap[H][ids[s]].feature.id) ? true : false; } else { highlightIsTrue = undefined; featureExists = false; } try { if (highlightIsTrue === undefined) { //if highlight feature has not been drawn nor checked or everything was destroyed... if ((!userEditableOnly || seg.arePropertiesEditable()) && RSel.checkSegment(rsh.HIGHLIGHTS[h].rule.expr, seg)) { //test against selection criteria countHighlighted++; if (countHighlighted === 1) { newFeatureArray.push(rsh_OL.createLineFeature(seg, hlLineStyle[h], h, countHighlighted * userSuppressRoad)); } else { newFeatureArray.push(rsh_OL.createLineFeature(seg, hlLineStyleOvrlp[h], h)); } } else { rsh_OL._featureMap[H][ids[s]] = { highlight: false }; } } else if (recheckRules) { //just the rules need to be rechecked if ((!userEditableOnly || seg.arePropertiesEditable()) && RSel.checkSegment(rsh.HIGHLIGHTS[h].rule.expr, seg)) { //test against selection criteria countHighlighted++; if (!featureExists) { //wasn't highlighted or feature has been destroyed, but now passes rule... if (countHighlighted === 1) { newFeatureArray.push(rsh_OL.createLineFeature(seg, hlLineStyle[h], h, countHighlighted * userSuppressRoad)); } else { newFeatureArray.push(rsh_OL.createLineFeature(seg, hlLineStyleOvrlp[h], h)); } } } else { //seg fails selection criteria, //but it is still drawn... if (highlightIsTrue) delFeatureArray.push(rsh_OL._featureMap[H][ids[s]].feature); //delFeatureArray.concat(rsh_OL.getFeaturesByAttribute('rshID', 'h'+h+'-'+ids[s])); //flag it for removal } //TODO: add in 'false' assignment to highlight property } else if (highlightIsTrue) { countHighlighted++; if (redraw === h || !featureExists) { //feature has been deleted by OL //if (!featureExists) { if (countHighlighted === 1) { newFeatureArray.push(rsh_OL.createLineFeature(seg, hlLineStyle[h], h, countHighlighted * userSuppressRoad)); } else { newFeatureArray.push(rsh_OL.createLineFeature(seg, hlLineStyleOvrlp[h], h)); } //} //if (featureExists) { //redraw all previously highlighted segs... //delFeatureArray.concat(rsh_OL.getFeaturesByAttribute('rshID', 'h'+h+'-'+ids[s])); //flag it for removal // delFeatureArray.push(rsh_OL._featureMap[H][ids[s]].feature); //} /*if (countHighlighted === 1) { //TODO: Optimize this later... rsh_OL.redrawLineFeature(rsh_OL._featureMap[H][ids[s]].feature, hlLineStyle[h], countHighlighted * userSuppressRoad); } else { rsh_OL.redrawLineFeature(rsh_OL._featureMap[H][ids[s]].feature, hlLineStyleOvrlp[h]); } }*/ } } } catch (err) {} } else { if (rsh_OLu._featureMap[H][ids[s]]) { highlightIsTrue = rsh_OLu._featureMap[H][ids[s]].highlight; //featureExists = (rsh_OL._featureMap[H][ids[s]].feature) ? true : false; featureExists = (rsh_OLu._featureMap[H][ids[s]].feature && rsh_OLu._featureMap[H][ids[s]].feature.id) ? true : false; } else { highlightIsTrue = undefined; featureExists = false; } try { if (highlightIsTrue === undefined) { //if highlight feature has not been drawn nor checked or everything was destroyed... if ((!userEditableOnly || seg.arePropertiesEditable()) && RSel.checkSegment(rsh.HIGHLIGHTS[h].rule.expr, seg)) { //test against selection criteria countHighlighted++; newFeatureArrayu.push(rsh_OLu.createLineFeature(seg, hlLineStyle[h], h)); } else { rsh_OLu._featureMap[H][ids[s]] = { highlight: false }; } } else if (recheckRules) { //just the rules need to be rechecked if ((!userEditableOnly || seg.arePropertiesEditable()) && RSel.checkSegment(rsh.HIGHLIGHTS[h].rule.expr, seg)) { //test against selection criteria countHighlighted++; if (!featureExists) newFeatureArrayu.push(rsh_OLu.createLineFeature(seg, hlLineStyle[h], h)); } else { //seg fails selection criteria, //but it is still drawn... if (highlightIsTrue) delFeatureArrayu.push(rsh_OLu._featureMap[H][ids[s]].feature); } //TODO: add in 'false' assignment to highlight property } else if (highlightIsTrue) { countHighlighted++; if (redraw === h || !featureExists) newFeatureArrayu.push(rsh_OLu.createLineFeature(seg, hlLineStyle[h], h)); } } catch (err) {} } } // TODO: add a feature changed check and redraw rather than destroy+draw //-------------------------------------------------------------------------------------- // Check if script should highlight "background roads" (i.e., roads that don't meet any of the other highlight criteria) if (userBgHighlight) { if (rsh_OL._featureMap.bg[ids[s]]) { //highlightIsTrue = rsh_OL._featureMap.bg[ids[s]].highlight; featureExists = (rsh_OL._featureMap.bg[ids[s]].feature && rsh_OL._featureMap.bg[ids[s]].feature.id) ? true : false; } else { //highlightIsTrue = undefined; featureExists = false; } if (countHighlighted === 0 && (!featureExists || redraw===true)) { newBgFeatureArray.push(rsh_OL.createLineFeature(seg, bgLineStyle, 'bg')); } else if (countHighlighted !== 0 && featureExists) { requestAnimationFrame(function() { rsh_OL.destroyFeaturesBy('id', parseInt(ids[s])) }); } } //------------------------------------------------------------------------------------ } //isOnScreen } //while rsh_OL.destroyFeatureArray(delFeatureArray); rsh_OLu.destroyFeatureArray(delFeatureArrayu); if (newBgFeatureArray.length) //requestAnimationFrame(function() { rsh_OL.addFeatures(newBgFeatureArray); //}); if (newFeatureArray.length) rsh_OL.addFeatures(newFeatureArray); if (newFeatureArrayu.length) rsh_OLu.addFeatures(newFeatureArrayu); rsh_OLu.setZIndex(334); //---------------------------------------------------------------------------------------------- // Check if time to perform cleanup var H; for (h of rsh.rulesStack) { H = String(h); if (rsh_OL._featureMap[H] && Object.keys(rsh_OL._featureMap[H]).length > 10000) rsh_OL.cleanupFeatureMap(H); if (rsh_OLu._featureMap[H] && Object.keys(rsh_OLu._featureMap[H]).length > 10000) rsh_OLu.cleanupFeatureMap(H); } if (Object.keys(rsh_OL._featureMap.bg).length > 10000) rsh_OL.cleanupFeatureMap('bg'); zoomLevel = currentZoom; } else { rsh.showSessionHighlights = false; } } // ----------------------------------------------------------------------------- var addStuffToLayerObject = function(layer) { layer._clone = rsh._clone; layer._getFeatureMapCounts = function() { var fTotal, fKey, idCounter, id; for (fKey in this._featureMap) { fTotal = Object.keys(this._featureMap[fKey]).length; console.debug('# of unique segs logged in _featureMap["' + fKey + '"] =', fTotal); idCounter = 0; for (id in this._featureMap[fKey]) { if (this._featureMap[fKey][id].feature && this._featureMap[fKey][id].feature.id) idCounter++; } console.debug('# of drawn highlights on map from _featureMap["' + fKey + '"] =', idCounter); } console.debug('Total segments in data extent =', W.map.segmentLayer.features.length); console.debug('OL features array length =', this.features.length); }; layer.redrawLineFeature = function(feature, lineOptions, stack) { if (stack === undefined) { /* do nothing */ } else if (stack === 1) { lineOptions.strokeOpacity = 0.9; } this.drawFeature(feature, lineOptions); }; layer.createLineFeature = function(seg, lineOptions, rshIndex, stack) { var segID = parseInt(seg.attributes.id), vertices = seg.geometry.components, rshID = 'h' + rshIndex + '-' + segID, feature; if (stack === undefined) { /* do nothing */ } else if (stack === 1) { lineOptions.strokeOpacity = 0.9; } //this._featureMap[String(rshIndex)][segID] = { highlight: true, bounds: seg.geometry.bounds}; feature = new OL.Feature.Vector(new OL.Geometry.LineString(vertices), { id: segID, rshIndex: rshIndex, rshID: rshID, stack: stack }, lineOptions); //this._featureMap[String(rshIndex)][segID] = { highlight: true, bounds: feature.attributes.geometry.bounds}; //this._featureMap[String(rshIndex)][segID] = { highlight: true, feature: {attributes: {geometry: {bounds: feature.attributes.geometry.bounds}}}}; this._featureMap[String(rshIndex)][String(segID)] = { highlight: true, feature: feature }; return feature; }; layer.unsetFeatureMappings = function(featArray) { //var numRemoved = 0; try { featArray.map(function(a, i) { //numRemoved++; this._featureMap[String(a.attributes.rshIndex)][String(a.attributes.id)] = undefined; }); } catch (err) {} //console.info('WMERSH:','Feature(s) unmapped =',numRemoved); }; layer.destroyFeatureArray = function(featArray) { if (featArray.length) { this.destroyFeatures(featArray); this.unsetFeatureMappings(featArray); } }; layer.destroyFeaturesBy = function(attrName, attrVal) { var destroyThese = this.getFeaturesByAttribute(attrName, attrVal); this.destroyFeatureArray(destroyThese); }; layer.destroyFeaturesByMapKey = function(featMapKey) { if (featMapKey === undefined) featMapKey = rsh.idx; var destroyThese = this.getFeaturesByAttribute('rshIndex', parseInt(featMapKey)); if (destroyThese.length) { this._featureMap[String(featMapKey)] = {}; this.destroyFeatures(destroyThese); } }; layer.destroyAllFeatureMaps = function() { this.destroyFeatures(); this._featureMap = { bg: {} }; }; layer.cleanupFeatureMap = function(featMapKey) { if (featMapKey === undefined) featMapKey = String(rsh.idx); console.debug('cleanupFeatureMap("' + featMapKey + '")'); requestAnimationFrame(function() { //calculate current active bounds at same scale var currentCenter = W.map.getCenter(), //lon-lat scalingFactor = W.map.getResolutionForZoom(3), //OL.Util.getScaleFromResolution(W.map.getResolutionForZoom(zoomOfDataExtent), W.map.baseLayer.units), newDataBounds = W.map.getExtent().scale(scalingFactor, currentCenter), featKeysSegID = Object.keys(layer._featureMap[String(featMapKey)]), nfeatKeysSegID = featKeysSegID.length, f, segID, segBounds, _featureMapSaver = {}; for (f = featKeysSegID; f--;) { segID = featKeysSegID[f]; segBounds = (layer._featureMap[String(featMapKey)][segID].feature && layer._featureMap[String(featMapKey)][segID].feature.id) ? layer._featureMap[String(featMapKey)][segID].feature.attributes.geometry.bounds : undefined; //segBounds = this._featureMap[String(featMapKey)][segID].bounds; if (segBounds !== undefined && newDataBounds.intersectsBounds(segBounds)) { //_featureMapSaver[segID] = { highlight: true, bounds: segBounds}; _featureMapSaver[segID] = layer._featureMap[String(featMapKey)][segID]; } } console.debug(Object.keys(_featureMapSaver).length); layer._featureMap[String(featMapKey)] = _featureMapSaver; }); }; layer.isOnScreen = function(segID) { return W.map.getExtent().intersectsBounds(W.model.segments.objects[segID].geometry.bounds); }; return layer; }; var initHighlightsLayer = function() { var rsh_ol = new OL.Layer.Vector("Road Selector Highlights", { //W.map.getLayersByName("Road Selector Highlights")[0] rendererOptions: { zIndexing: true }, uniqueName: '__RSel_Highlights', displayInLayerSwitcher: true, draggable: false }); I18n.translations.en.layers.name.__RSel_Highlights = 'Road Selector Highlights'; Waze.map.addLayer(rsh_ol); Waze.map.addControl(new OL.Control.DrawFeature(rsh_ol, OL.Handler.Path)); rsh_ol.setZIndex(501); rsh_ol.setVisibility(rsh.showSessionHighlights); rsh_ol._featureMap = { bg: {} }; rsh_ol = addStuffToLayerObject(rsh_ol); return rsh_ol; }; var initUnderRoadsLayer = function() { var rsh_olu = new OL.Layer.Vector("Road Selector Highlights (Under)", { //W.map.getLayersByName("Road Selector Highlights")[0] rendererOptions: { zIndexing: true }, uniqueName: '__RSel_Highlights_Under', displayInLayerSwitcher: false, draggable: false }); I18n.translations.en.layers.name.__RSel_Highlights_Under = 'Road Selector Highlights (Under)'; Waze.map.addLayer(rsh_olu); Waze.map.addControl(new OL.Control.DrawFeature(rsh_olu, OL.Handler.Path)); rsh_olu.setZIndex(334); rsh_olu.setVisibility(rsh.showSessionHighlights); rsh_olu._featureMap = { "0": {} }; rsh_olu = addStuffToLayerObject(rsh_olu); return rsh_olu; }; // ----------------------------------------------------------------------------- var styleMenuDelete = function(that) { delete rsh.STYLES[that.parentNode.name]; that.parentNode.parentNode.remove(); localStorage.RSHighlights_Styles = JSON.stringify(rsh.STYLES); }; var populateStylesMenu = function() { var styleNames = Object.keys(rsh.STYLES).sort(), numStyles = styleNames.length, stylesHTML = '', sn, styleName; // The following is for when populateStylesMenu() is being called for the purpose of resetting the presets $('.rsh-styles-menu li>a>span.fa-times').parents('li').remove(); // Add styles to menu for (sn = 0; sn < numStyles; sn++) { styleName = styleNames[sn]; if (!/^Default$/.test(styleName)) { stylesHTML += '<li><a name="' + styleName + '">' + styleName + '<span class="fa fa-times fa-pull-right fa-lg"></span></a></li>'; } } $('.rsh-styles-menu ul').append(stylesHTML); // Add event listeners $('.rsh-styles-menu li>a').click(stylesMenuActions); $('.rsh-styles-menu li>a>span.fa-times').click(function(e) { e.stopPropagation(); styleMenuDelete(this); }); }; var stylesMenuActions = function(e) { switch (this.name) { case 'saveas': e.stopPropagation(); var inputName = prompt("Please enter a name for your style:"), styleNames = Object.keys(rsh.STYLES); if (inputName !== null) { rsh.STYLES[inputName] = rsh.HIGHLIGHTS[rsh.idx].style; if (!styleNames.contains(inputName)) { $('.rsh-styles-menu ul').append('<li><a name="' + inputName + '">' + inputName + '<span class="fa fa-times fa-pull-right fa-lg"></span></a></li>'); $('.rsh-styles-menu li>a[name="' + inputName + '"]').click(stylesMenuActions); $('.rsh-styles-menu li>a[name="' + inputName + '"]>span.fa-times').click(function(e) { e.stopPropagation(); styleMenuDelete(this); }); } localStorage.RSHighlights_Styles = JSON.stringify(rsh.STYLES); } break; case 'saveasdefault': rsh.STYLES.Default = rsh._clone(rsh.HIGHLIGHTS[rsh.idx].style); rsh.STYLES.Default.strokeColor = '#FFFF00'; localStorage.RSHighlights_Styles = JSON.stringify(rsh.STYLES); break; case 'resetdefault': rsh.STYLES.Default = rsh._clone(rsh.presets.defaultLineStyle); localStorage.RSHighlights_Styles = JSON.stringify(rsh.STYLES); break; case 'resetpresets': $.extend(rsh.STYLES, rsh.presets.lineStyles); localStorage.RSHighlights_Styles = JSON.stringify(rsh.STYLES); populateStylesMenu(); break; default: var strokeColor = rsh.HIGHLIGHTS[rsh.idx].style.strokeColor; rsh.HIGHLIGHTS[rsh.idx].style = rsh._clone(rsh.STYLES[this.name]); rsh.HIGHLIGHTS[rsh.idx].style.strokeColor = strokeColor; updatePanelFromPrefs(); rsh.triggerUpdate = true; requestAnimationFrame(Highlight); } }; var copyToClipboard = function(str) { var $temp = $("<input>"); $("body").append($temp); $temp.val(str).select(); document.execCommand("copy"); $temp.remove(); }; var pasteFromClipboard = function() { var $temp = $("<input>"), str; $("body").append($temp); $temp.focus(); $temp.val('.').select(); document.execCommand("paste"); str = $temp.val(); $temp.remove(); console.debug(str); return str; } // ----------------------------------------------------------------------------- var InitRSelHighlights = function() { // return rsh var checkForSessionSave = function() { // return rsh rsh = new RSH(); // Load autosaved parameters from sessionStorage if (sessionStorage.RSHighlights) { rsh.importHighlights(JSON.parse(sessionStorage.RSHighlights)); //console.debug("WMERSH: Imported data from previous session.") if (rsh.HIGHLIGHTS.length === 0 || (rsh.HIGHLIGHTS.length !== 0 && rsh.HIGHLIGHTS[0].rule.text === undefined)) rsh = new RSH(); } //if (rsh.loadedSavedSession) {if (rsh.HIGHLIGHTS[rsh.lastIdx].rule !== null) rsh.addHighlighter(); } if (rsh.showSessionHighlights === undefined) rsh.showSessionHighlights = false; if (localStorage.RSHighlights === undefined) localStorage.RSHighlights = '{}'; if (localStorage.RSHighlights_Styles === undefined) { localStorage.RSHighlights_Styles = ''; } else { rsh.addStyles(); } if (localStorage.RSHighlights_Prefs === undefined) { localStorage.RSHighlights_Prefs = ''; } else { document.getElementById('cbRSHSuppRoad').checked = /&s/.test(localStorage.RSHighlights_Prefs); //suppress document.getElementById("cbRSHBgHighlight").checked = /&t/.test(localStorage.RSHighlights_Prefs); //trace document.getElementById("cbRSHignoreEditable").checked = /&e/.test(localStorage.RSHighlights_Prefs); //ignore editable only } return rsh; }; var setupRSHInterface = function() { // CSS for RSel Highlights var rshCSS = document.createElement("style"); rshCSS.type = "text/css"; // UI panel rshCSS.innerHTML = '.rsh-main-panel { min-width: 285px; width: 100%; background-color: #BEDCE5; border-style: solid; border-width: 2px; border-collapse: collapse; ' + ' padding: 5px 8px; border-color: #93C4D3; margin-top: 15px; border-radius: 5px; vertical-align: middle; }\n' + '.rsh-dropbar-panel { color: #444; background: transparent; border-style: inset; margin-top: 0; border-width: 2px; border-color: rgba(146,194,209,.60); ' + ' border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; }\n' + // border-bottom-left-radius: 5px; border-bottom-right-radius: 5px0 '.rsh-dropbar-panel table { border: 0; width: 100%; display: inline-table;}\n' + '.rsh-dropbar-panel td { font-size: 7pt; font-weight: 600; line-height: 6px; padding: 0px 4px 4px 4px; margin: 0; vertical-align: middle; color: #59899E;}\n' + '.rsh-arrow-up { width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 5px solid black; }\n' + '.rsh-arrow-down { width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid black; }\n' + 'div.rsh-vcentered { position: relative; padding: 2px 5px 3px; width: 100%; display: inline-block; vertical-align: middle; text-align: left; }\n' + '.rsh-dropbar-contents {display: block; padding: 8px 0px 0px; margin: 0; border: 0; background-color: rgba(216, 231, 236, 0.3); }\n' + 'select.rsh-dropbar-panel { height:20px; width: 100px; background-color: #FFF; border: 1px solid #959595; margin: 0px 0px 2px 2px; padding: 0; }\n' + 'input.rsh-dropbar-panel { height:20px; width: 36px; background-color: #FFF; border: 1px solid #C1C1C1; border-radius: 0; margin: 0px 0px 2px 2px; padding: 0; }\n' + '.rsh-dropbar-header { color: #000; border-bottom: 1px solid #c0c0c0; }\n' + 'a.rsh-link { font-weight: 600; text-decoration: none; width: 130%; padding: 1px 3px;}' + '.rsh-btn-container>label { padding-left: 4px; }' + '.rsh-btn-row { position: relative; display: block; vertical-align: middle; margin: auto; padding: 0; }\n' + '.rsh-btn-container { height: 25px; position: relative; display: inline-block; padding: 0; margin: 0px 1%; vertical-align: middle; }\n' + '.rsh-checkbox-row { margin-top: -3px; padding: 0px 10px 2px; }\n' + '.rsh-checkbox-row label { padding-top: 3px; margin: 0; color: #F5F5F5; font-size: 11px; }\n' + '.rsh-checkbox-row:hover, .rsh-checkbox-row:focus {background-color: rgba(255, 255, 255, 0.18); }\n'; // Buttons rshCSS.innerHTML += '.rsh-btn-color {width: 24px; height: 25px; position: absolute; top: 0px; left: 0px; margin-right: -3px; padding: 0; z-index: 0; }\n' + '.rsh-btn.rsh-btn-color { border-radius: 5px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; z-index: 0; padding: 2px; padding-left: 4px; border-bottom: 2px solid rgba(0,0,0,0.3); }\n' + '.rsh-btn.rsh-btn-highlight { border-radius: 4px; width: 75px; border-top-left-radius: 0px; border-bottom-left-radius: 0px; }\n' + //border-bottom: solid 2px #E4E4E4; border-top: 0; border-right: 0; border-left: 0; }' + '.rsh-btn-counter { z-index: 0 !important; height: 25px; width: 25px; padding: 3px 4px; font-size: 14px; line-height: 1.4; cursor: pointer; ' + ' color: #88888E; background-color: #CFE0E6; border-left: solid 1px #7AB0BE; border-bottom: 0; ' + ' box-shadow: inset 0px 2px 4px -1px rgba(0,0,0,0.2), inset 0px -2px 4px -2px rgba(0,0,0,0.2) }\n' + '.rsh-btn-counter:hover, .rsh-btn-counter:active, .rsh-btn-counter.active, .rsh-btn-counter:focus {opacity: 0.7;}\n' + '.rsh-btn-inner { border-radius: 5px; background-color: white; display: inline-block; }\n' + '.rsh-btn { border-radius: 4px; font-size: 12px; line-height: 1.5; height: 25px; font-weight: bold; color: #396179; padding: 3px;}\n' + //background-color: #93C1D3; '.rsh-btn:hover { opacity: 0.95; color: #08648E; }\n' + //color: #48758C; '.rsh-btn.disabled { color: #6C9AAE; cursor: not-allowed !important; opacity: .85;}\n' + //box-shadow: inset 0px 0px 0px 1px rgba(0,0,0,0.05); '.rsh-btn:active, .rsh-btn:focus, .rsh-btn.active { background-image: none; outline: 0; -webkit-box-shadow: none; box-shadow: none; }\n' + 'a#btnRSHClearAll.rsh-btn { padding-top: 2px; border-left: solid 1px #B099B1; color: white; width: 25px;}\n' + 'a#btnRSHClearAll.rsh-btn.disabled { background-color: #ff8383; color: #FFE0E0 !important; opacity: 0.75;}\n' + '.rsh-btn>.tooltip-inner { font-weight: bold; }\n' + '.fa-bars.rsh-icn, .rsh-styles-menu .fa-caret-square-o-down { margin-left: 0; margin-right: -1%; line-height: 0.9; padding: 4px; height: 25px; display: inline-block; vertical-align: middle; color: #59899E; font-size: 17px; text-shadow: 0px 1px 0px rgba(255,255,255,0.5); border: 1px solid transparent; }\n' + '.rsh-icn:hover, .rsh-icn:active, .rsh-icn:focus { border: 1px solid rgb(132, 174, 191); border-radius: 4px; cursor: pointer; }\n' + '.rsh-styles-menu>ul>li>a { padding: 2px 15px; padding-right: 10px; white-space: normal; cursor: pointer; }\n' + '.rsh-styles-menu .fa-times:hover, .rsh-styles-menu .fa-times:focus, .rsh-styles-menu .fa-times:active {color: #ed503b;}\n' + '.rsh-styles-menu .fa-times { margin-top: 2.5px; color: lightgray; }\n' + '.btn-group .rsh-btn2 { font-size: 13px; width: 21px; height: 22px; padding: 1px; border-width: 1px; border-radius: 3px; color: #396179; background: transparent; }\n' + '.btn-group .rsh-btn2:focus, .btn-group .rsh-btn2:active { border: 1px solid #00BCD4; z-index: 2;}\n'; rshCSS.innerHTML += // Dropup expressions menu '.dropdown-menu.rsh-expr-menu { background-color: inherit; right: -92px; padding: 1px; border-radius: 2px; font-size: 10px; max-height: 400px; width: 301px; overflow-x: hidden; overflow-y: auto;}\n' + '.dropdown-menu.rsh-expr-menu>li { background-color: inherit; border-bottom: 1px solid #93C4D3; margin: 0; padding: 0; }\n' + '.rsh-expr-item { opacity: 1; background-color: white; word-wrap: break-word; white-space: normal; padding-top: 8px; padding-bottom: 8px; padding-left: 10px; padding-right: 10px; position: relative;}\n' + '.dropdown-menu.rsh-expr-menu .rsh-expr-item:active, .rsh-expr-item.active, .rsh-expr-item:focus { background-color: #BEDCE5; }\n' + '.rsh-expr-item:hover { opacity: 1; cursor: pointer; background-color: #D4e7ed; }\n' + '.rsh-expr-menu.rsh-dragging .rsh-expr-item:hover { background-color: white; }\n' + '.rsh-expr-item>.rsh-expr-color { pointer-events: none; text-shadow: 0px 0px 1px gray; margin-left: 2px; margin-right: 6px; margin-top: 2px; position: absolute; font-size: 11px; }\n' + '.rsh-expr-item>.rsh-expr-text { pointer-events: auto; z-index: 1; position: relative; left: 20px; display: inline-block; width: 265px; margin-right: -10px; padding-right: 22px; margin-bottom: -8px}\n' + '.rsh-expr-item>.rsh-expr-dragicn { z-index: 10; font-size: 14px; color: #BBB; margin-top: -3px; font-weight: normal; width: 25px; text-align: center; position: absolute; padding-right: 5px; right: 2px;}\n' + '.rsh-expr-item.active>.rsh-expr-dragicn { color: #8FB3C1; }\n' + '.rsh-expr-item>.rsh-expr-dragicn:hover, .rsh-expr-item>.rsh-expr-dragicn:focus { cursor: move; color: #81ABBB }\n' + '.dropdown-menu.rsh-expr-menu .rsh-expr-item.rsh-drag { box-shadow: inset 0px -8px 0px #93C4D3 }\n' + '.rsh-expr-menu>li.rsh-hidden {display: none; height: 9px; }\n' + '.rsh-expr-menu.rsh-dragging>li.rsh-hidden { z-index: 1; display: block; height: auto;}\n' + '.rsh-expr-menu>li.rsh-hidden>.rsh-expr-item { width: 100%; padding: 0; }\n' + '.rsh-expr-menu>li.rsh-hidden>.rsh-expr-item>.rsh-expr-text { pointer-events: auto; display: block; height: 8px; margin: 0; padding: 0}\n' + '.rsh-expr-menu>li.rsh-hidden>.rsh-expr-item.rsh-drag { box-shadow: inset 0px -20px 0px #93C4D3; }\n' + '.rsh-expr-menu>li.rsh-hidden>.rsh-expr-item.rsh-drag>.rsh-expr-text { pointer-events: auto; height: 20px;}\n' + '.rsh-expr-item .rsh-expr-spacer { pointer-events: none; width: 100%; display: block; height: 8px; }\n' + '.rsh-expr-item.rsh-drag .rsh-expr-spacer { height: 16px; }\n' + '.rsh-expr-menu.rsh-dragging>li:nth-child(2)>div.rsh-expr-item { margin-top: -9px;}\n' + '.rsh-expr-export {margin-left: 4px; cursor: pointer; color: #598991; }\n' + '#rshMenuNote { white-space: normal; background-color: rgba(0,0,0,0.85); border-radius: 5px; color: white; font-size: 10px; padding: 5px; font-weight: 600; font-family: "Open Sans", "Alef", helvetica, sans-serif;}\n' + '.rsh-expr-del { pointer-events: auto; opacity: 0; background-color: inherit; display: inline-block; position: absolute; font-size: 16px; line-height: 0.9; color: #ed503b; z-index: 1;}\n' + '.rsh-expr-del:hover, .rsh-expr-del:active, .rsh-expr-del:focus, .rsh-expr-item:hover .rsh-expr-del {opacity: 1;}\n' + ''; // 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; }\n' + '.rsh-icn-indark { display: inline-block; margin: 0; padding: 0; background-color: #555; }\n' + '.rsh-icn-inwhite { display: inline-block; margin: 0; padding: 0; background-color: #FFF; }\n' + '.rsh-icn-inclear { display: inline-block; margin: 0; padding: 0; background-color: transparent; }\n' + '.rsh-icn-brdark { display: block; margin: 0; padding: 0; background-color: #555; }\n' + '.rsh-icn-brclear { display: block; margin: 0; padding: 0; background-color: transparent; }\n' + '.rsh-icn-ingray { display: inline-block; margin: 0; padding: 0; background-color: #bbbbbb; }\n' + '.rsh-icn-2x2 { width: 2px; height: 2px }\n' + '.rsh-icn-3x2 { width: 3px; height: 2px }\n' + '.rsh-icn-2x1 { width: 2px; height: 1px }\n' + '.rsh-icn-1x2 { width: 1px; height: 2px }\n' + '.rsh-icn-1x1 { width: 1px; height: 1px }\n' + '.rsh-icn-3x3 { width: 3px; height: 3px }\n' + '.rsh-icn-4x3 { width: 4px; height: 3px }\n' + '.rsh-icn-13x1 { width: 13px; height: 1px }\n'; document.body.appendChild(rshCSS); //------------------------------------------------------------------ // CSS-HTML Icons // [weight, opacity, stroke type, end cap, dash size, dash gap] 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">' + ' <div class="rsh-btn-inner" style="margin-right: 6px;">' + ' <input type="color" id="selRSHColors" value="#FFFF00" class="btn rsh-btn-color" style="opacity: 0; float: left; position: relative; padding: 0; -webkit-appearance: none; -moz-appearance: none;" ' + ' list="colors" data-toggle="tooltip" title="Highlighter color">' + // highlight color ' <datalist id="colors">' + ' <option>#CCFF66</option><option>#28F311</option><option>#CCFFCC</option><option>#00F2F2</option><option>#0037D1</option>' + ' <option>#FFFF00</option><option>#F5950E</option><option>#E1201B</option><option>#FF00FF</option><option>#8000FF</option>' + ' </datalist></input>' + ' <div id="selRSHColorBtn" class="btn rsh-btn rsh-btn-color" style="pointer-events: none; z-index: 1; background-color: rgb(128, 0, 255);">' + '<span class="fa fa-caret-down fa-lg"></span>' + '</div>' + ' <button id="btnRSHighlight" class="btn btn-primary rsh-btn rsh-btn-highlight active" data-toggle="tooltip" title="Update map highlights"> Highlight</button>' + // HIGHLIGHT ' </div><div class="rsh-btn-inner" style="margin-right: 6px;">' + ' <a id="btnRSHClearTop" style="width: 26px;" class="btn btn-primary rsh-btn disabled" data-toggle="tooltip" title="Erase highlights of current selection" href="javascript:void(0)">' + // undo eraser ' <span class="fa fa-eraser fa-lg"></span></a>' + ' </div></div>' + ' <div class="rsh-btn-container"><div class="btn-group rsh-btn-inner">' + ' <a id="btnRSHAdd" style="width: 24px;" class="btn btn-primary rsh-btn" data-toggle="tooltip" title="Add another highlighter" href="javascript:void(0)">' + // Add layer ' <span class="fa fa-plus"></span></a>' + ' <div id="btnRSHmenu" class="btn-group dropup">' + ' <div id="btnRSHCount" class="btn rsh-btn-counter" title="View highlighting rules" data-toggle="tooltip">' + // layer counter ' <span id="txtRSHCount">0</span></div>' + // [#] ' <ul class="dropdown-menu pull-right dropdown-menu-right list-group rsh-expr-menu">' + ' <li></li>' + ' </ul>' + ' </div>' + ' <a id="btnRSHSubtract" style="width: 24px;" class="btn btn-primary rsh-btn disabled" data-toggle="tooltip" title="Delete highlighter" href="javascript:void(0)">' + // border-right: solid 1px #7AB0BE; ' <span class="fa fa-minus"></span></a>' + ' <a id="btnRSHClearAll" class="btn btn-danger rsh-btn" data-toggle="tooltip" title="Delete all highlighters" href="javascript:void(0)">' + ' <span class="fa fa-trash fa-lg"></span></a>' + ' </div></div>' + ' <div id="icnRSHmoreOpts" class="fa fa-bars pull-right rsh-icn" title="More highlight options" data-toggle="tooltip"></div>' + '</div></div>'); $("#divRSHmainPanel").append( '<div id="rshMoreOptions" style="display: none;">' + '<div class="rsh-btn-row" style="margin: 6px -8px 0px; padding: 6px 0px; border-top: 1px solid #93C4D3; border-bottom: 1px solid #93C4D3; background-color: rgba(89, 137, 158, 0.7);">' + ' <div class="rsh-checkbox-row"><div class="checkbox-inline">' + ' <input type="checkbox" id="cbRSHSuppRoad" class="btn" style="margin-right: 5px; margin-bottom: 7px;"></input>' + // Suppress roads ' <label for="cbRSHSuppRoad" data-toggle="tooltip" title="Hides roads by making highlights opaque, while still allowing highlights drawn over the same segment to have an overlapping colors effect.">Suppress apperance of roads under highlights</label>' + ' </div></div>' + ' <div class="rsh-checkbox-row">' + ' <div class="checkbox-inline" style="width: 210px;">' + ' <input type="checkbox" id="cbRSHBgHighlight" class="btn">' + ' <label for="cbRSHBgHighlight" data-toggle="tooltip" title="Activate this option to visually block irrelevant segments that do not meet the selection criteria. You can turn off the Roads layer to give all non-highlighted segments a uniform appearance.">Trace over non-highlighted roads with a single color</label>' + // Trace background roads checkbox ' </div>' + ' <div style="display: inline-block">' + ' <input type="color" id="selRSHBgRoadColor" value="#c0c0c0" class="btn rsh-btn-drop" ' + // trace color ' list="bgRdcolors" style="position: relative; right: -5px; top: 3px; width: 44px; height: 21px; padding: 3px 7px; background-color: rgba(255, 255, 255, 0.25); border: 1px solid transparent; " ' + ' 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>' + ' </div></div>' + ' <div class="rsh-checkbox-row"><div class="checkbox-inline">' + ' <input type="checkbox" id="cbRSHignoreEditable" class="btn" style="margin-right: 5px; margin-bottom: 7px;"></input>' + // Ignore editable only checkbox ' <label for="cbRSHignoreEditable" data-toggle="tooltip" title="Checking this option will make RSel Highlights ignore the Editable only checkbox.">Always highlight both editable and noneditable segments</label>' + ' </div></div>' + '</div>' + '<div class="form-inline" style="vertical-align: middle; margin: 6px -2px 0px -2px; position: relative;">' + '<select id="selRSHighlighter" class="form-control" style="font-weight: normal; font-size: 12px; padding: 0; width: 55%; max-width: 145px; height: 24px;"><option value="" selected>(Saved Highlighters)</option></select>' + '<div class="btn-group" style="position: absolute; right: 1px; top: 1px;">' + '<a id="applyRSHighlighter" title="Load up selected highlighter set" style="width: 23px;" class="btn btn-primary rsh-btn2"><i class="fa fa-upload fa-fw"></i></a>' + '<a id="saveRSHighlighter" title="Save highlighting session" class="btn btn-primary rsh-btn2"><i class="fa fa-save fa-fw"></i></a>' + '<a id="delRSHighlighter" title="Delete selected highlighter set" class="btn btn-primary rsh-btn2"><i class="fa fa-times fa-fw"></i></a>' + '<a id="importRSHighlighter" title="Import highlighters from clipboard" class="btn btn-primary rsh-btn2"><i class="fa fa-sign-in fa-flip-horizontal fa-fw"></i></a>' + '<a id="exportRSHighlighter" title="Export selected highlighter set" class="btn btn-primary rsh-btn2"><i class="fa fa-reply fa-flip-horizontal fa-fw"></i></a>' + '<a id="exportAllRSHighlighter" title="Export all highlighter sets" class="btn btn-primary rsh-btn2"><i class=" fa fa-reply-all fa-flip-horizontal fa-fw"></i></a>' + '</div>' + '</div>' + '</div>'); //if (isFirefox) $('#cbRSHBgHighlight').parent().css('padding-left','28px'); // Additional style options drop menu $("#RSselection").append('\ <div id="divRSHdropbarPanel" class="rsh-dropbar-panel" style="min-width: 285px; width: 100%;">\ <div id="divRSHdropbarHeader" class="rsh-vcentered">\ <div style="display: inline-block">\ <a id="aRSHdropbarLinkShow" class="rsh-link" href="javascript:void(0);" style="display: none;"><i class="fa fa-chevron-down"></i> Show additional style options</a>\ <a id="aRSHdropbarLinkHide" style="display: inline-block;" class="rsh-link" href="javascript:void(0);"><i class="fa fa-chevron-up"></i> Hide additional style options</a>\ </div>\ <div class="dropdown fa-pull-right rsh-styles-menu open" style="display: none;">\ <button class="btn dropdown-toggle" type="button" data-toggle="dropdown" style="height: 100%; padding: 0; background: transparent; border: 0;">\ <span class="fa fa-caret-square-o-down" style="font-size: 11px; height: 100%; line-height: 0.5;"></span></button>\ <ul class="dropdown-menu" style="top: 80%; left: initial; right: -3px; border-radius: 2px; font-size: 10px; min-width: 140px; max-width: 140px;">\ <li><a name="saveas" href="#">Save Style As...</a></li>\ <li><a name="saveasdefault" href="#">Save As Default</a></li>\ <li><a name="resetdefault" href="#">Reset Default Style</a></li>\ <li><a name="resetpresets" href="#">Reload Original Presets</a></li>\ <li class="divider"></li>\ <li class="dropdown-header" style="padding: 2px 15px; font-size: 9px; text-transform: uppercase;">Apply Style Preset</li>\ <li><a name="Default" href="#">Default*</a></li>\ </ul>\ </div>\ </div>' + //closing for divRSHdropbarHeader '<div id="divRSHdropbarContents" class="rsh-dropbar-contents">' + // table for additional style options within dropbar panel '<table>' + '<tr><td style="vertical-align: bottom">Thickness</td><td style="vertical-align: bottom">Opacity</td>' + '<td rowspan=4>' + // inner table for dash style options '<table style="padding-left: 3px; padding-right: 3px; border-left: 1px solid #CBE1E8 !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 size</td><td style="vertical-align: bottom">Gap size</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 length (relative)" disabled \></td>' + '<td>' + rshIcons[5] + '<input type="number" id="numRSHGapScale" style="border-radius: 3px; background-color: #EFEFEF;" class="rsh-dropbar-panel" min="0.1" max="10" value="1" step="0.1" title="Dash gap size (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>' + '' + '<select id="selRSHCap" class="rsh-dropbar-panel" style="border-color: #B4B4B4; width: 69px;"></select></td>' + '<td><input type="checkbox" id="cbRSHunderRoads" class="btn" style="margin: 0px 7px 7px 5px;"><label for="cbRSHunderRoads" class="fa fa-road fa-2x" data-toggle="tooltip" title="Draw highlight color under segments rather than on top" style="color: #333; background-color: gold; padding: 1px 0px 1px;"></label></td>' + '</tr></table>' + '</div></div>'); $("div#divRSHmainPanel [data-toggle=tooltip], div#divRSHdropbarPanel [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: 600; !important"></div></div>' }); }; //--------------------------------------- // Insert UI into side-panel of WME under RSel tab setupRSHInterface(); toggleDropBarMenu(true); //--------------------------------------- // Retrieve any data from saved sessionStorage var rsh = checkForSessionSave(); //--------------------------------------- var rsh_saved = JSON.parse(localStorage.RSHighlights), highlighterNames = Object.keys(rsh_saved).sort(); for (var k of highlighterNames) { document.getElementById("selRSHighlighter").add(new Option(k)); } // select from highlighter list if name is present if (rsh.highlighterName) document.getElementById("selRSHighlighter").value = rsh.highlighterName; //--------------------------------------- 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])); } return rsh; }; //------------------------------------------------------------------------ rsh = InitRSelHighlights(); // Update panel updatePanelFromPrefs(); //rsh.recreateExpressionText(); updateExprMenu(rsh.loadedSavedSession); if (rsh.loadedSavedSession) disableEraseButton(); populateStylesMenu(); rsh_seg = []; // Add Road Selector Highlights layer to map rsh_OL = initHighlightsLayer(); rsh_OLu = initUnderRoadsLayer(); userBgHighlight = document.getElementById("cbRSHBgHighlight").checked; userEditableOnly = !(!document.getElementById("cbRSEditable").checked || document.getElementById('cbRSHignoreEditable').checked); userSuppressRoad = document.getElementById("cbRSHSuppRoad").checked; //------------------------------------------------------------------------ // Setup event listeners/triggers var closeExprMenu = function() { document.getElementById('btnRSHmenu').classList.remove('open'); window.removeEventListener('click', closeExprMenu, false); //$('.rsh-expr-menu').css('display',''); //document.getElementsByClassName('rsh-expr-menu')[0].removeEventListener('mouseleave', closeExprMenu, false); }; document.getElementById('btnRSHCount').addEventListener('click', function(e) { if (document.getElementById('btnRSHmenu').classList.toggle('open')) { updateExprMenu(); setTimeout(function() { window.addEventListener('click', closeExprMenu, false) }, 100); } else { setTimeout(function() { window.removeEventListener('click', closeExprMenu, false); //$('.rsh-expr-menu').css('display',''); //document.getElementsByClassName('rsh-expr-menu')[0].addEventListener('mouseleave', closeExprMenu, false); }, 100); } }, false); /* document.getElementById('btnRSHCount').addEventListener('mouseenter', function() { updateExprMenu(); //document.getElementById('btnRSHmenu').classList.add('open'); document.getElementsByClassName('rsh-expr-menu')[0].style.display = 'block'; setTimeout(function() { document.getElementsByClassName('rsh-expr-menu')[0].addEventListener('mouseleave', closeExprMenu, false); //window.addEventListener('click', closeExprMenu, false); }, 100); }, false); */ //-------- document.getElementById("selRSHColors").addEventListener('click', function() { document.getElementById("selRSHColors").onchange = function() { rsh.triggerUpdate = true; updatePrefsFromPanel(); updateHighlighterColorButton(); drawHighlights([],rsh.idx); document.getElementById("selRSHColors").onchange = null; }; }, false); //----- document.getElementById('btnRSHighlight').onclick = drawHighlights; document.getElementById('btnRSHighlight').ondblclick = function() { requestAnimationFrame(function() { Highlight(true) }) }; //destroy all features then highlight document.getElementById('btnRSHAdd').onclick = addNewHighlighter; document.getElementById('btnRSHSubtract').onclick = deleteHighlighter; document.getElementById('btnRSHClearTop').onclick = eraseHighlight; document.getElementById('btnRSHClearAll').onclick = clearAllHighlights; document.getElementById('icnRSHmoreOpts').onclick = function() { rsh.storeCurrentExpression(); ($('#rshMoreOptions').css('display') === 'none') ? $('#rshMoreOptions').css('display', 'block') : $('#rshMoreOptions').css('display', 'none'); }; document.getElementById('aRSHdropbarLinkHide').onclick = function() { toggleDropBarMenu(true) }; document.getElementById('aRSHdropbarLinkShow').onclick = function() { toggleDropBarMenu(false) }; //-------------------------------------------------------------------- document.getElementById('cbRSHSuppRoad').onclick = function() { userSuppressRoad = this.checked; if (this.checked) localStorage.RSHighlights_Prefs = localStorage.RSHighlights_Prefs.replace(/&s|$/, '&s'); //suppress else localStorage.RSHighlights_Prefs = localStorage.RSHighlights_Prefs.replace(/&s/, ''); requestAnimationFrame(function() { Highlight(true) }); }; document.getElementById("cbRSHBgHighlight").onclick = function() { userBgHighlight = this.checked; if (userBgHighlight) localStorage.RSHighlights_Prefs = localStorage.RSHighlights_Prefs.replace(/&t|$/, '&t'); //trace else localStorage.RSHighlights_Prefs = localStorage.RSHighlights_Prefs.replace(/&t/, ''); //rsh.triggerUpdate = true; //updatePrefsFromPanel(); requestAnimationFrame(function() { Highlight(true) }); }; document.getElementById('cbRSHignoreEditable').onclick = function() { userEditableOnly = !(!document.getElementById("cbRSEditable").checked || this.checked); if (!userEditableOnly) localStorage.RSHighlights_Prefs = localStorage.RSHighlights_Prefs.replace(/&e|$/, '&e'); //ignore editable only else localStorage.RSHighlights_Prefs = localStorage.RSHighlights_Prefs.replace(/&e/, ''); requestAnimationFrame(function() { Highlight('a') }); //recheck all rules and only draw if feature does not exist }; document.getElementById("cbRSEditable").addEventListener('click', function() { userEditableOnly = !(!this.checked || document.getElementById('cbRSHignoreEditable').checked); requestAnimationFrame(function() { Highlight('a') }); //recheck all rules and only draw if feature does not exist }, false); //-------------------------------------------------------------------- document.getElementById("selRSHDash").addEventListener('click', function() { document.getElementById("selRSHDash").onchange = function() { rsh.triggerUpdate = true; updatePrefsFromPanel(); updateDashButtons(); requestAnimationFrame(Highlight); //document.getElementById("selRSHDash").onblur = function () { document.getElementById("selRSHDash").onchange = null; //document.getElementById("selRSHDash").onblur = null; //}; }; }, false); //-------- document.getElementById("selRSHCap").addEventListener('click', function() { document.getElementById("selRSHCap").onchange = function() { rsh.triggerUpdate = true; updatePrefsFromPanel(); requestAnimationFrame(Highlight); //document.getElementById("selRSHCap").onblur = function () { document.getElementById("selRSHCap").onchange = null; //document.getElementById("selRSHCap").onblur = null; //}; }; }, false); //-------- document.getElementById('cbRSHunderRoads').onclick = function() { rsh.triggerUpdate = true; updatePrefsFromPanel(); requestAnimationFrame(Highlight); }; document.getElementById("numRSHOpacity").onfocusin = function() { document.getElementById("numRSHOpacity").onchange = function() { rsh.triggerUpdate = true; document.getElementById("numRSHOpacity").onblur = function() { updatePrefsFromPanel(); requestAnimationFrame(Highlight); document.getElementById("numRSHOpacity").onchange = null; document.getElementById("numRSHOpacity").onblur = null; }; }; }; //-------- document.getElementById("numRSHScaleWidth").onfocusin = function() { document.getElementById("numRSHScaleWidth").onchange = function() { rsh.triggerUpdate = true; document.getElementById("numRSHScaleWidth").onblur = function() { updatePrefsFromPanel(); requestAnimationFrame(Highlight); document.getElementById("numRSHScaleWidth").onchange = null; document.getElementById("numRSHScaleWidth").onblur = null; }; }; }; //-------- document.getElementById("numRSHDashSizeScale").onfocusin = function() { document.getElementById("numRSHDashSizeScale").onchange = function() { rsh.triggerUpdate = true; document.getElementById("numRSHDashSizeScale").onblur = function() { updatePrefsFromPanel(); requestAnimationFrame(Highlight); document.getElementById("numRSHDashSizeScale").onchange = null; document.getElementById("numRSHDashSizeScale").onblur = null; }; }; }; //-------- document.getElementById("numRSHGapScale").onfocusin = function() { document.getElementById("numRSHGapScale").onchange = function() { rsh.triggerUpdate = true; document.getElementById("numRSHGapScale").onblur = function() { updatePrefsFromPanel(); requestAnimationFrame(Highlight); document.getElementById("numRSHGapScale").onchange = null; document.getElementById("numRSHGapScale").onblur = null; }; }; }; document.getElementById('selRSHighlighter').onchange = function() { if (!!this.value && this.value !== '') { $('#applyRSHighlighter').css('color', '#2196F3'); } else { $('#applyRSHighlighter').css('color', ''); } }; document.getElementById('applyRSHighlighter').onclick = function() { $('#applyRSHighlighter').css('color', ''); if (document.getElementById("selRSHighlighter").value !== 0) { var rsh_saved = JSON.parse(localStorage.RSHighlights.replace(/("#[\w\d]{6})\s?/ig, '$1')), highlighterName = document.getElementById("selRSHighlighter").value, mergeSet = confirm('Press "OK" to add the highlighters to your current session or "Cancel" to clear your highlights and then load the set.'), numInSet; if (mergeSet) { numInSet = rsh.HIGHLIGHTS.length; rsh.HIGHLIGHTS = rsh.HIGHLIGHTS.concat(rsh_saved[highlighterName].HIGHLIGHTS); rsh.lastIdx = rsh.HIGHLIGHTS.length - 1; //rsh.idx = rsh.lastIdx; for (var i = numInSet, iLength = rsh.lastIdx; i<=iLength; i++) { rsh.rulesStack.push(i); } rsh.loadedSavedSession = true; rsh.triggerUpdate = true; rsh.HIGHLIGHTS.forEach(function(a){a.rule.tryOnce = false;}); } else { //RSelExprParser.rselButtons.clear(); rsh.importHighlights(rsh_saved[highlighterName]); rsh.addStyles(); } rsh.highlighterName = highlighterName; updatePanelFromPrefs(); drawHighlights([],'import'); document.getElementById('icnRSHmoreOpts').click(); setTimeout(function(){document.getElementById('btnRSHCount').click()}, 100); } }; document.getElementById('saveRSHighlighter').onclick = function() { $('#applyRSHighlighter').css('color', ''); var rsh_saved = JSON.parse(localStorage.RSHighlights), selectedName = document.getElementById('selRSHighlighter').value; var highlighterName = prompt('Please enter a name for this highlighter:', selectedName), addHighlighterName = document.createElement('option'); if (highlighterName !== null && highlighterName !== '') { rsh_saved[highlighterName] = { HIGHLIGHTS: rsh.HIGHLIGHTS }; localStorage.RSHighlights = JSON.stringify(rsh_saved).replace(/("#[\w\d]{6})\s?/ig, '$1'); var matchIndex = null, o, highlighters = document.getElementById('selRSHighlighter').options; for (o = highlighters.length; o--;) { if (new RegExp('^' + highlighterName + '$').test(highlighters[o].value)) { matchIndex = o; break; } } if (matchIndex !== null) { document.getElementById("selRSHighlighter").options[matchIndex].remove(); } addHighlighterName.appendChild(document.createTextNode(highlighterName)); document.getElementById("selRSHighlighter").appendChild(addHighlighterName); document.getElementById("selRSHighlighter").selectedIndex = document.getElementById("selRSHighlighter").options.length - 1; $('#applyRSHighlighter').css('color', ''); } }; document.getElementById('delRSHighlighter').onclick = function() { $('#applyRSHighlighter').css('color', ''); var rsh_saved = JSON.parse(localStorage.RSHighlights), highlighterName = document.getElementById("selRSHighlighter").value, selIdx = document.getElementById("selRSHighlighter").selectedIndex, userConfirm; if (selIdx) { //prevent the first option from being deleted userConfirm = confirm('Are you sure that you want to delete "' + highlighterName + '"?'); if (userConfirm) { document.getElementById("selRSHighlighter").removeChild(document.getElementById("selRSHighlighter").options[selIdx]); delete rsh_saved[highlighterName]; document.getElementById("selRSHighlighter").selectedIndex = 0; localStorage.RSHighlights = JSON.stringify(rsh_saved); } } }; document.getElementById('importRSHighlighter').onclick = function() { var str = prompt('Paste exported RSel Highlights text string:'), rsh_import, rsh_saved, msgStr, importedSomething = false, selectedName = document.getElementById('selRSHighlighter').value, htmlStr = '<div id="rshMenuNote" style="position: absolute;bottom: 110%;width: 100px;left: -44px;white-space: normal;background-color: rgba(0,0,0,0.85);border-radius: 5px;color: white;font-size: 10px;padding: 5px;">'; if (str !== null && str !== '') { rsh_import = JSON.parse(str.replace(/("#[\w\d]{6})\s?/ig, '$1')); //remove space after color HEX, in case it was copied from slack if (rsh_import.constructor === Object) { for (var r in rsh_import) { //do a quick check if (!rsh_import[r].HIGHLIGHTS) { importedSomething = false; break; } else { importedSomething = true; } } if (importedSomething) { // Check for pre-existing names var matchIndex = [], o, i, importNames = Object.keys(rsh_import), highlighters = document.getElementById('selRSHighlighter').options; for (o = highlighters.length; o--;) { for (i = importNames.length; i--;) { if (new RegExp('^' + importNames[i] + '$').test(highlighters[o].value)) { matchIndex.push(o); } } } //remove duplicate names if (matchIndex.length) { for (var m = 0, mLength = matchIndex.length; m < mLength; m++) { document.getElementById("selRSHighlighter").options[matchIndex[m]].remove(); } } //add names for all imported highlighters for (var n of importNames) { document.getElementById("selRSHighlighter").add(new Option(n)); } //get saved data from localStorage rsh_saved = JSON.parse(localStorage.RSHighlights); //merge saved data $.extend(true, rsh_saved, rsh_import); console.debug(rsh_saved); // save to localStorage localStorage.RSHighlights = JSON.stringify(rsh_saved); //put up import note var numImported = importNames.length; msgStr = 'Imported ' + numImported + ' set(s) of highlighters'; setTimeout(function() { $('#importRSHighlighter').append(htmlStr + msgStr + '</div>') }, 300); setTimeout(function() { $('#rshMenuNote').remove() }, 5000); $('#applyRSHighlighter').css('color', '#2196F3'); } } else { htmlStr = '<div id="rshMenuNote" style="position: absolute;bottom: 110%;width: 100px;left: -44px;white-space: normal;background-color: rgba(0,0,0,0.85);border-radius: 5px;color: red;font-size: 10px;padding: 5px;">'; msgStr = 'Import unsuccessful. The pasted string did not contain the expected data.'; setTimeout(function() { $('#importRSHighlighter').append(htmlStr + msgStr + '</div>') }, 300); setTimeout(function() { $('#rshMenuNote').remove() }, 5000); } document.getElementById('rshMenuNote').onclick = function(){this.remove()}; } //else prompt was cancelled }; document.getElementById('exportRSHighlighter').onclick = function(){ $('#applyRSHighlighter').css('color', ''); exportRSelHighlighter(); }; document.getElementById('exportAllRSHighlighter').onclick = function() { $('#applyRSHighlighter').css('color', ''); var rsh_saved = JSON.parse(localStorage.RSHighlights), htmlStr = '<div id="rshMenuNote" style="position: absolute;bottom: 110%;width: 100px;left: -52px;white-space: normal;background-color: rgba(0,0,0,0.85);border-radius: 5px;color: white;font-size: 10px;padding: 5px;">'; copyToClipboard(JSON.stringify(rsh_saved).replace(/("#[\w\d]{6})\s?/ig, '$1')); setTimeout(function() { $('#exportAllRSHighlighter').append(htmlStr + 'Data for all saved highlighters were copied to your clipboard</div>') }, 300); setTimeout(function() { $('#rshMenuNote').remove() }, 5000); document.getElementById('rshMenuNote').onclick = function(){this.remove()}; }; //------------------------------------------------------------------------ window.addEventListener("beforeunload", function() { sessionStorage.RSHighlights = JSON.stringify(rsh); }, false); var timeoutCanceller; // Event listeners to redraw highlights /*Waze.map.events.register("zoomend", Waze.map, function () { //rsh_OL.adjustBounds(W.model.segments.currentDataBounds); clearTimeout(timeoutCanceller1); clearTimeout(timeoutCanceller2); Highlight('d'); //destroy all features, but not featureMaps timeoutCanceller1 = setTimeout(function(){ Highlight(); //continue and draw any segs that were missing in first round timeoutCanceller2 = setTimeout(function(){ Highlight(null); //continue and now also add segments that are off-screen }, 1000); }, 1000) });*/ Waze.map.events.register("moveend", Waze.map, function(evt) { clearTimeout(timeoutCanceller); //console.debug('WMERSH','********* Timeout Cancelled **********'); setTimeout(function() { //console.debug('WMERSH','Highlight() - 1'); Highlight(); //assumes no changes in rules, highlight styles, etc timeoutCanceller = setTimeout(function() { //console.debug('WMERSH','Highlight() - 2'); Highlight(); timeoutCanceller = setTimeout(function() { //console.debug('WMERSH','Highlight() - 3'); Highlight(); /*timeoutCanceller = setTimeout(function() { Highlight(null) }, 800);*/ }, 800); }, 800); }, 100); }); Waze.model.actionManager.events.register("afterundoaction", null, function(evt) { //console.debug(evt); evt.object.actions.map(function(a) { if (a.changedSegStates) { a.changedSegStates.map(function(segID) { if (segID && segID.constructor === Number) { rsh_OL.destroyFeaturesBy('id', segID); rsh_OLu.destroyFeaturesBy('id', segID); } }); Highlight(); } else if (a.segment && a.segment.attributes && a.segment.attributes.id) { rsh_OL.destroyFeaturesBy('id', a.segment.attributes.id); rsh_OLu.destroyFeaturesBy('id', a.segment.attributes.id); Highlight(); } }); }); Waze.model.actionManager.events.register("afterclearactions", null, function(evt) { Highlight(true); rsh_OLu.setZIndex(334); }); Waze.map.segmentLayer.events.register('featuremodified', W.model.segments.objects, function(evt) { var segID = evt.feature.model.attributes.id; rsh_OL.destroyFeaturesBy('id', segID); rsh_OLu.destroyFeaturesBy('id', segID); Highlight(); }); //Waze.map.events.register("move", Waze.map, Highlight); //Waze.map.events.register("changelayer", Waze.map, Highlight); //Waze.map.events.register("visibilitychanged", Waze.map, Highlight); //------------------------------------------------------------------------ zoomLevel = W.map.zoom; setTimeout(Highlight, 5000); } /*//////////////////////////////////////////////////////////////////////////*/ 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) */ RSel = unsafeWindow.RoadSelector; if (typeof $ === 'undefined') { $ = unsafeWindow.$; W = Waze = unsafeWindow.Waze; } 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);