WME Context Menu

A right-click popup menu for editing segments. Currently integrates with WME Speedhelper and Road Selector to help make it even easier and faster to edit the map.

As of 05.04.2016. See ბოლო ვერსია.

// ==UserScript==
// @name            WME Context Menu
// @namespace       https://greasyfork.org/users/11629-TheLastTaterTot
// @version         0.3.5
// @description     A right-click popup menu for editing segments. Currently integrates with WME Speedhelper and Road Selector to help make it even easier and faster to edit the map.
// @author          TheLastTaterTot
// @include         https://editor-beta.waze.com/*editor/*
// @include         https://www.waze.com/*editor/*
// @exclude         https://www.waze.com/*user/editor/*
// @grant           none
// @run-at          document-end
// ==/UserScript==
/* jshint -W097 */

//--------------- DEBUG ----------------
var DEBUG = false;
function cmlog() {
    if (DEBUG) {
	    var debugStyle, debugCode,
	    	args = arguments,
	        argArray = Object.keys(args).map(function(key) {
	            return args[key];
	        });
	        if (argArray[0].constructor === Array) {
	        	debugStyle = argArray[0][1];
	        	debugCode = parseInt(argArray[0][0]);
	        }

	    if (debugCode === DEBUG) {
	    	argArray = argArray.splice(1);

	    	switch (debugStyle) {
	    		case 1: // examine functions
	    			logCss = 'background: #444; color: #6FF';
	    			break;
	    		case 2: // examine eventListeners
	    			logCss = 'background: #CCC; color: #048';
	    			break;
	    		default:
	    			logCss = 'background: #EEE; color: #000; font-weight: bold;';
	    	}

	    	console.debug('%cWMECM: %s', logCss, argArray.join(' '));
	    }
	}
}
//---------------------------------------
var roadTypes = {
        1: "Street",
        2: "Primary Street",
        3: "Freeway",
        4: "Ramp",
        5: "Walking Trail",
        6: "Major Highway",
        7: "Minor Highway",
        8: "Dirt road / 4X4 Trail",
        10: "Pedestrian Boardwalk",
        15: "Ferry",
        16: "Stairway",
        17: "Private Road",
        18: "Railroad",
        19: "Runway/Taxiway",
        20: "Parking Lot Road"
    },
    menuResetEvent_RSel = false,
    menuResetEvent_SL = true,
    contextMenuSettings,
    slSavedMaxElementTotal = 0,
    slSavedMenuElementFlags = false,
    speedhelperHeight = null;
    speedhelperHTML = false,
    SL_imperial = {
        countries: ['UK', 'US', 'MU', 'AR', 'IC', 'JE', 'GQ', 'CJ', 'BM', 'LI'],
        mph2kph: 1.609344,
        kph2mph: 0.621371192,
        convertUnits: null
    },
    changeEvent = new Event('change', { 'bubbles': true }),
    isFirefox = !!~navigator.userAgent.indexOf('irefox'),
    minVersion = '0.3';

try {
	if (isFirefox && localStorage.WME_ContextMenuFF) {
		window.alert('WME Context Menu has been updated to be fully functional in FireFox.\n\nThank you for your patience!');
	    localStorage.removeItem('WME_ContextMenuFF');
	}
} catch(err) {}

//-----------------------------------------------
if (localStorage.WME_ContextMenu) {
    contextMenuSettings = JSON.parse(localStorage.WME_ContextMenu);
    if (!contextMenuSettings.hidden) contextMenuSettings.hidden = {};
} else {
    contextMenuSettings = {
        clipboard: 0,
        position: 0,
        pin: false,
        countries: [],
        version: 0,
        hidden: {}
    };
    localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);
}
//-----------------------------------------------

CMenuVersion = {
    currentVersion: GM_info.script.version,
    lastVersionString: function(){return contextMenuSettings.version;},
    convertToNumericVersion: function(versionString) {
        var vMult = [8000, 400, 20, 1],
            versionNumeric = 0;

        if (versionString) {
            if (versionString.constructor === Array) {
                if (versionString.length === 1) {
                    versionString = versionString[0];
                } else {
                    console.error('WMECM:', 'versionString is an array with more than 1 element.');
                }
            }
            return versionString.match(/(\d)+/g).map(function(d, i) {
                versionNumeric += d * vMult[i];
            }), versionNumeric;
        } else {
            return null;
        }
    },
    getLastVersionValue: function() {
        return this.convertToNumericVersion(this.lastVersionString());
    },
    updateVersionString: function() {
    	contextMenuSettings.version = this.currentVersion;
        localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);
    },
    isUpToDate: function(minimumVersionString) {
        var minVersionVal = this.convertToNumericVersion(minimumVersionString),
            lastVersionVal = this.getLastVersionValue();
        return (lastVersionVal >= minVersionVal) ? true : false;
    }
};
////////////////////////////////////////////////////////////////////////////////////////////////
var getUnique = function (objArray) {
    var isNotDuplicate = function (comparisonList, checkThisName) {
        var isNotDup = true;
        try {
            for (var c = 0, cLength = comparisonList.length; c < cLength; c++) {
                if (comparisonList[c] === checkThisName) isNotDup = false;
            }
        } catch (err) {
            console.error(err);
        }
        return isNotDup;
    };

    try {
        var uniqObjs = [];
        for (var r = objArray.length; r--;) {
            if (isNotDuplicate(uniqObjs, objArray[r])) uniqObjs.push(objArray[r]); // && objArray[r] !== ''
        }
        return uniqObjs;
    } catch (err) {
        console.error(err);
    }
};

//----------------------------------------------------------------------
var addPasteItems = function(attrName) {
    if (/primaryStreet|primaryCity/.test(attrName)) {
        clipboard = document.getElementById('input_' + attrName);
        if (clipboard) {
            pasteOption = document.createElement('dd');
            pasteOption.className = 'cm-paste';
            pasteOption.innerHTML = clipboard.value;
            pasteOption.name = clipboard.value;
            //$('#'+attrName+'>dt').after('<dd class="cm-paste" name="' + clipboard.value + '">' + clipboard.value +'</dd>');

            $('#' + attrName + ' .cm-paste').remove();

            if (document.querySelector('#' + attrName + '>dd')){
                document.getElementById(attrName).insertBefore(pasteOption, document.querySelector('#' + attrName + '>dd'));
            } else {
                document.getElementById(attrName).appendChild(pasteOption);
            }
            pasteOption.addEventListener('click', function(e) { pasteTo(e, this.parentNode.id, this.name); }, false);
        }
    }
};

var pasteTo = function (e, opt, val) {
    try { // (temporary) really dumb way via DOM nodes:
        switch (opt) {
            case 'cm_primaryStreet':
                $('.address-edit-icon').click();
                $('#emptyStreet').prop('checked',false).change();
                $('.form-control.streetName').val(val).change();
                if (!$('.form-control[name=cityName]').val().length) $('#emptyCity').prop('checked',true).change();
                if ($('.form-control[name=stateID]').length) $('.form-control[name=stateID]').val(W.model.states.top.id);
                if ($('.form-control[name=countryID]').length) $('.form-control[name=countryID]').val(W.model.countries.top.id);
                $('.action-buttons>.save-button').click();
                break;
            case 'cm_primaryCity':
                $('.address-edit-icon').click();
                $('#emptyCity').prop('checked',false).change();
                $('.form-control[name=cityName]').val(val).change();
                if (!$('.form-control.streetName').val().length) $('#emptyStreet').prop('checked',true).change();
                if ($('.form-control[name=stateID]').length) $('.form-control[name=stateID]').val(W.model.states.top.id);
                if ($('.form-control[name=countryID]').length) $('.form-control[name=countryID]').val(W.model.countries.top.id);
                $('.action-buttons>.save-button').click();
                break;
            /*case 'cm_state':
                var $selState = $('.form-control[name=stateID]>option');
                $selState.map(function(i,opt){ console.info(opt.text); if (opt.text === val) opt.prop('selected','') });
                break;*/
        }
    } catch(err) { console.error(err); }
};

var getTopCityName = function() {
	return W.model.cities.objects[W.model.segments.topCityID].name;
};

//----------------------------------------------------------------------
var addStreetAndCityToRSel = function (segIds, caseSelection) {
    var opAndOr = parseInt(document.getElementById('cmOpAddOr').value),
        opNot = document.getElementById('cmOpNot').classList.contains('active'),
        addConjunction = !!document.getElementById('outRSExpr').innerHTML.length, s;

    switch (caseSelection ^ 0) {
        case 'primaryStreet':
        case 0:
            if (segIds.primaryStreet.length !== 0) {
                for (s = segIds.primaryStreet.length; s--;) {
                    if (addConjunction) {
                        if (opAndOr) {
                            document.getElementById('btnRSOr').click();
                        } else {
                            document.getElementById('btnRSAnd').click();
                        }
                    }
                    if (opNot) document.getElementById('btnRSNot').click();
                    //set to primary
                    document.getElementById('selRSAlttStreet').value = 0;
                    document.getElementById('selRSAltCity').value = 0;

                    document.getElementById('btnRSLBkt').click();
                    document.getElementById('inRSStreet').value = Waze.model.streets.objects[segIds.primaryStreet[s]].name;
                    document.getElementById('btnRSAddStreet').click();
                    //document.getElementById('btnRSAnd').click();
                    document.getElementById('inRSCity').value = Waze.model.cities.objects[Waze.model.streets.objects[segIds.primaryStreet[s]].cityID].name;
                    document.getElementById('btnRSAddCity').click();
                    document.getElementById('btnRSRBkt').click();
                }
            }
            break;
        case 'altStreets':
        case 1:
            if (segIds.altStreets.length !== 0) {
                for (s = segIds.primaryStreet.length; s--;) {
                    if (addConjunction) {
                        if (opAndOr) {
                            document.getElementById('btnRSOr').click();
                        } else {
                            document.getElementById('btnRSAnd').click();
                        }
                    }

                    if (opNot) document.getElementById('btnRSNot').click();
                    //set to alt
                    document.getElementById('selRSAlttStreet').value = 1;
                    document.getElementById('selRSAltCity').value = 1;

                    document.getElementById('btnRSLBkt').click();
                    document.getElementById('inRSStreet').value = Waze.model.streets.objects[segIds.altStreets[s]].name;
                    document.getElementById('btnRSAddStreet').click();
                    //document.getElementById('btnRSAnd').click();
                    document.getElementById('inRSCity').value = Waze.model.cities.objects[Waze.model.streets.objects[segIds.altStreets[s]].cityID].name;
                    document.getElementById('btnRSAddCity').click();
                    document.getElementById('btnRSRBkt').click();
                }
            }
            break;
        case 'anyStreet':
        case 2:
            addStreetAndCityToRSel(segIds, 0); //primary
            addStreetAndCityToRSel(segIds, 1); //alt
            break;
    }
};

var addStreetNameToRSel = function (segNames, inputFieldId, altSelId, altVal, addBtnId) {
    var opAndOr = parseInt(document.getElementById('cmOpAddOr').value),
        opNot = document.getElementById('cmOpNot').classList.contains('active'),
        addConjunction = !!document.getElementById('outRSExpr').innerHTML.length;

    for (var n = 0, nLength = segNames.length; n < nLength; n++) {
        if (addConjunction) {
            if (opAndOr) {
                document.getElementById('btnRSOr').click();
            } else {
                document.getElementById('btnRSAnd').click();
            }
        }
        if (opNot) document.getElementById('btnRSNot').click();

        document.getElementById(inputFieldId).value = segNames[n];
        document.getElementById(altSelId).value = altVal ^ 0;
        document.getElementById(addBtnId).click();
        addConjunction = true;
    }
};

var addRoadTypeToRSel = function (ids, inputFieldId, addBtnId) {
    var opAndOr = parseInt(document.getElementById('cmOpAddOr').value),
        opNot = document.getElementById('cmOpNot').classList.contains('active'),
        addConjunction = !!document.getElementById('outRSExpr').innerHTML.length;

    document.getElementById('btnRSLBkt').click();
    for (var n = 0, nLength = ids.length; n < nLength; n++) {
        if (addConjunction) {
            if (opAndOr) {
                document.getElementById('btnRSOr').click();
            } else {
                document.getElementById('btnRSAnd').click();
            }
        }
        if (opNot) document.getElementById('btnRSNot').click();

        document.getElementById(inputFieldId).value = ids[n];
        document.getElementById(addBtnId).click();
        addConjunction = true;
    }
    document.getElementById('btnRSRBkt').click();
};

var copyToClipboard = function(id,str) {
    var $hiddenText;
    if (!document.getElementById('input_' + id)) {
        $hiddenText = $('<input>');
        $hiddenText.prop('id','input_' + id);
        $hiddenText.css('position','fixed');
        $hiddenText.css('top','-100px');
        $hiddenText.css('left','-1000px');
        $hiddenText.css('opacity',0);
        $("body").append($hiddenText);
    } else {
        $hiddenText = $('#input_' + id);
    }
    $hiddenText.val(str).select();
    document.execCommand("copy");
    //$temp.remove();
};

var copyTo = function (e, opt, val) {
    if (document.getElementById('cmPinMenu').value) e.stopPropagation();

    if (document.getElementById('cmClipboard').value) {
        copyToClipboard(opt,val);
        if (document.getElementById('cmPinMenu').value) addPasteItems(opt);
    } else {
        try {
            switch (opt) {
                case 'cm_priSC':
                    if (document.getElementById('cmRoadType').classList.contains('active')) {
                        addRoadTypeToRSel(getUnique(val.roadType), 'selRSRoadType', 'btnRSAddRoadType');
                        document.getElementById('btnRSAnd').click();
                        document.getElementById('btnRSLBkt').click();
                        addStreetAndCityToRSel(val, 0);
                        document.getElementById('btnRSRBkt').click();
                    } else {
                        addStreetAndCityToRSel(val, 0);
                    }
                    break;
                case 'cm_altSC':
                    if (document.getElementById('cmRoadType').classList.contains('active')) {
                        addRoadTypeToRSel(getUnique(val.roadType), 'selRSRoadType', 'btnRSAddRoadType');
                        document.getElementById('btnRSAnd').click();
                        document.getElementById('btnRSLBkt').click();
                        addStreetAndCityToRSel(val, 1);
                        document.getElementById('btnRSRBkt').click();
                    } else {
                        addStreetAndCityToRSel(val, 1);
                    }
                    break;
                case 'cm_anySC':
                    if (document.getElementById('cmRoadType').classList.contains('active')) {
                        addRoadTypeToRSel(getUnique(val.roadType), 'selRSRoadType', 'btnRSAddRoadType');
                        document.getElementById('btnRSAnd').click();
                        document.getElementById('btnRSLBkt').click();
                        addStreetAndCityToRSel(val, 2);
                        document.getElementById('btnRSRBkt').click();
                    } else {
                        addStreetAndCityToRSel(val, 2);
                    }
                    break;
                case 'cm_priS':
                    if (document.getElementById('cmRoadType').classList.contains('active')) {
                        addRoadTypeToRSel(getUnique(val.roadType), 'selRSRoadType', 'btnRSAddRoadType');
                        document.getElementById('btnRSAnd').click();
                        document.getElementById('btnRSLBkt').click();
                        addStreetNameToRSel(getUnique(val.primaryStreet), 'inRSStreet', 'selRSAlttStreet', 0, 'btnRSAddStreet');
                        document.getElementById('btnRSRBkt').click();
                    } else {
                        addStreetNameToRSel(getUnique(val.primaryStreet), 'inRSStreet', 'selRSAlttStreet', 0, 'btnRSAddStreet');
                    }
                    break;
                case 'cm_altS':
                    if (document.getElementById('cmRoadType').classList.contains('active')) {
                        addRoadTypeToRSel(getUnique(val.roadType), 'selRSRoadType', 'btnRSAddRoadType');
                        document.getElementById('btnRSAnd').click();
                        document.getElementById('btnRSLBkt').click();
                        addStreetNameToRSel(getUnique(val.altStreets), 'inRSStreet', 'selRSAlttStreet', 1, 'btnRSAddStreet');
                        document.getElementById('btnRSRBkt').click();
                    } else {
                        addStreetNameToRSel(getUnique(val.altStreets), 'inRSStreet', 'selRSAlttStreet', 1, 'btnRSAddStreet');
                    }
                    break;
                case 'cm_anyS':
                    if (document.getElementById('cmRoadType').classList.contains('active')) {
                        addRoadTypeToRSel(getUnique(val.roadType), 'selRSRoadType', 'btnRSAddRoadType');
                        document.getElementById('btnRSAnd').click();
                        document.getElementById('btnRSLBkt').click();
                        addStreetNameToRSel(getUnique(val.primaryStreet), 'inRSStreet', 'selRSAlttStreet', 0, 'btnRSAddStreet');
                        addStreetNameToRSel(getUnique(val.altStreets), 'inRSStreet', 'selRSAlttStreet', 1, 'btnRSAddStreet');
                        document.getElementById('btnRSRBkt').click();
                    } else {
                        addStreetNameToRSel(getUnique(val.primaryStreet), 'inRSStreet', 'selRSAlttStreet', 0, 'btnRSAddStreet');
                        addStreetNameToRSel(getUnique(val.altStreets), 'inRSStreet', 'selRSAlttStreet', 1, 'btnRSAddStreet');
                    }
                    break;
                case 'cm_ids':
                    document.getElementById('inRSSegId').value = val;
                    document.getElementById('btnRSAddSegId').classList.add('btn-info');
                    break;
                case 'cm_primaryStreet':
                    document.getElementById('inRSStreet').value = val;
                    document.getElementById('selRSAlttStreet').value = 0;
                    document.getElementById('btnRSAddStreet').classList.add('btn-info');
                    break;
                case 'cm_altStreets':
                    document.getElementById('inRSStreet').value = val;
                    document.getElementById('selRSAlttStreet').value = 1;
                    document.getElementById('btnRSAddStreet').classList.add('btn-info');
                    break;
                case 'cm_altStreetsAND':
                    document.getElementById('inRSStreet').value = '';
                    break;
                case 'cm_altStreetsOR':
                    document.getElementById('inRSStreet').value = '';
                    break;
                case 'cm_primaryCity':
                    document.getElementById('inRSCity').value = val;
                    document.getElementById('selRSAltCity').value = 0;
                    document.getElementById('btnRSAddCity').classList.add('btn-info');
                    break;
                case 'cm_altCities':
                    document.getElementById('inRSCity').value = val;
                    document.getElementById('selRSAltCity').value = 1;
                    document.getElementById('btnRSAddCity').classList.add('btn-info');
                    break;
                case 'cm_state':
                    document.getElementById('inRSState').value = val;
                    document.getElementById('btnRSAddState').classList.add('btn-info');
                    break;
                case 'cm_roadType':
                    document.getElementById('selRSRoadType').value = val;
                    document.getElementById('btnRSAddRoadType').classList.add('btn-info');
                    break;
                case 'cm_updatedBy':
                    document.getElementById('inRSUpdtd').value = val;
                    document.getElementById('btnRSAddUpdtd').classList.add('btn-info');
                    break;
                case 'cm_createdBy':
                    document.getElementById('inRSCrtd').value = val;
                    document.getElementById('btnRSAddCrtd').classList.add('btn-info');
                    break;
                case 'cm_toConnections':
                    break;
            }
        } catch (err) {
            console.error(err);
        }

        // swap panel from selection panel
        try {
            document.getElementById('user-info').style.display = 'block';
            document.getElementById('edit-panel').style.display = 'none';
        } catch (err) {
            console.error(err);
        }

        // switch active tab-content
        try {
            document.querySelector('.tab-content>.active').classList.remove('active');
            document.getElementById('sidepanel-roadselector').classList.add('active');
        } catch (err) {}

        // switch active nav-tab
        try {
            document.querySelector('#user-tabs li.active').classList.remove('active');
            document.getElementById('tabRSel').classList.add('active');
        } catch (err) {}

        // switch to RSel editor tab
        try {
            document.getElementById('roadselector-tabs').children[1].classList.remove('active');
            document.getElementById('roadselector-tabs').children[0].classList.add('active');
            document.getElementById('roadselector-tab-content').children[1].classList.remove('active');
            document.getElementById('roadselector-tab-content').children[0].classList.add('active');
        } catch (err) { /* */ }
    }
};

var getAutoAddToRSelCase = function (addType) {
    var nameClass = ((document.getElementById('cm_pri').checked * document.getElementById('cm_pri').value) + (document.getElementById('cm_alt').checked * document.getElementById('cm_alt').value)) - 1;

    switch (addType) {
        case 'cm_S':
            switch (nameClass) {
                case 0: //primary
                    return 'cm_priS';
                case 1: //alt
                    return 'cm_altS';
                case 2: //any
                    return 'cm_anyS';
            }
            break;
        case 'cm_SC': // cm_SC street and city
            switch (nameClass) {
                case 0: //primary
                    return 'cm_priSC';
                case 1: //alt
                    return 'cm_altSC';
                case 2: //any
                    return 'cm_anySC';
            }
            break;
    }
};

var getSegmentProperties = function (selectedStuff) {
	cmlog([1,1], 'getSegmentProperties()');
    try {
        /*var roadTypeOpts = document.querySelector('#segment-edit-general select[name="roadType"]'),
            numRoadTypes = roadTypeOpts.options.length, r, roadTypes = {};
        for (r=0; r < numRoadTypes; r++) {
            roadTypes[roadTypeOpts[r].value] = roadTypeOpts[r].text;
        }*/
        var s, segments = selectedStuff.segments,
            s_altStObjKeys, s_altSt, s_toConnObjKeys, a, numAlts, k, numKeys;

        var s_ids = {
            ids: {},
            primaryStreet: {},
            altStreets: {},
            altCities: {},
            roadType: {},
            createdBy: {},
            updatedBy: {},
            toConnections: {},
            primaryCity: {},
            state: {},
            country: {}
        };

        for (s = segments.length; s--;) {
            if (segments[s].model.attributes.id)
                s_ids.ids[segments[s].model.attributes.id] = null;
            if (segments[s].model.attributes.primaryStreetID) {
                s_ids.primaryStreet[segments[s].model.attributes.primaryStreetID] = null;
                if (Waze.model.streets.objects[segments[s].model.attributes.primaryStreetID].cityID) {
                    s_ids.primaryCity[Waze.model.streets.objects[segments[s].model.attributes.primaryStreetID].cityID] = null;
                    if (Waze.model.cities.objects[Waze.model.streets.objects[segments[s].model.attributes.primaryStreetID].cityID].stateID)
                        s_ids.state[Waze.model.cities.objects[Waze.model.streets.objects[segments[s].model.attributes.primaryStreetID].cityID].stateID] = null;

                    s_ids.country[Waze.model.cities.objects[Waze.model.streets.objects[segments[s].model.attributes.primaryStreetID].cityID].countryID] = null;
                }
            }
            s_ids.roadType[segments[s].model.attributes.roadType] = null;
            s_ids.createdBy[segments[s].model.attributes.createdBy] = null;
            if (segments[s].model.attributes.updatedBy)
                s_ids.updatedBy[segments[s].model.attributes.updatedBy] = null;


            s_altSt = segments[s].model.attributes.streetIDs;
            numAlts = s_altSt.length;
            for (a = 0;  a < numAlts; a++) {
                try {
                    s_ids.altStreets[s_altSt[a]] = null;
                    s_ids.altCities[Waze.model.streets.objects[s_altSt[a]].cityID] = null;
                } catch(err) {}
            }

            s_toConnObjKeys = Object.keys(segments[s].model.attributes.toConnections);
            numKeys = s_toConnObjKeys.length;
            for (k = 0; k < numKeys; k++) {
                try {
                    if (s_toConnObjKeys[k] !== '') {
                        s_ids.toConnections[s_toConnObjKeys[k]] = segments[s].model.attributes.toConnections[s_toConnObjKeys[k]];
                    }
                } catch(err) {}
            }
        }

        var seg_ids = {},
            seg_names = {
                ids: [],
                primaryStreet: [],
                altStreets: [],
                altCities: [],
                roadType: [],
                createdBy: [],
                updatedBy: [],
                toConnections: [],
                primaryCity: [],
                state: [],
                country: []
            };

        for (var idKey in s_ids) {
            seg_ids[idKey] = Object.keys(s_ids[idKey]);
            numKeys = seg_ids[idKey].length;

            for (k = 0; k < numKeys; k++) {
                try {
                    if (seg_ids[idKey][k] !== '') {
                        switch (idKey) {
                            case 'primaryStreet':
                                seg_names[idKey][k] = Waze.model.streets.objects[seg_ids[idKey][k]].name;
                                break;
                            case 'primaryCity':
                                seg_names[idKey][k] = Waze.model.cities.objects[seg_ids[idKey][k]].name;
                                break;
                            case 'altStreets':
                                seg_names[idKey][k] = Waze.model.streets.objects[seg_ids[idKey][k]].name;
                                break;
                            case 'altCities':
                                seg_names[idKey][k] = Waze.model.cities.objects[seg_ids[idKey][k]].name;
                                break;
                            case 'state':
                                seg_names[idKey][k] = Waze.model.states.objects[seg_ids[idKey][k]].name;
                                break;
                            case 'roadType':
                                seg_names[idKey][k] = roadTypes[String(seg_ids[idKey][k])];
                                break;
                            case 'createdBy':
                                seg_names[idKey][k] = Waze.model.users.objects[seg_ids[idKey][k]].userName;
                                break;
                            case 'updatedBy':
                                seg_names[idKey][k] = Waze.model.users.objects[seg_ids[idKey][k]].userName;
                                break;
                        }
                    }
                } catch(err) {}
            }
        }
        return {
            ids: seg_ids,
            names: seg_names
        };
    } catch (err) {
        console.error(err);
    }
};

var addHotkeyListener = function() {
	cmlog([1,1], 'addHotkeyListener()');
    window.addEventListener('keydown', menuShortcutKeys, true);
    cmlog([2,2],'Adding global hotkey listener due to mouseenter');
};

var removeHotkeyListener = function() {
	cmlog([1,1], 'removeHotkeyListener()');
    window.removeEventListener('keydown', menuShortcutKeys, true);
    cmlog([2,2],'Removing global hotkey listener due to mouseleave');
};

var closeContextMenu = function () {
	cmlog([1,1], 'closeContextMenu()');
    try {
    	// remove unnecessary hotkey listeners
        window.removeEventListener('keydown', menuShortcutKeys, true);
		document.getElementById('cmContextMenu').removeEventListener('mouseenter', addHotkeyListener, false);
		document.getElementById('cmContextMenu').removeEventListener('mouseleave', removeHotkeyListener, false);
		cmlog([2,2],'Closing menu, so removing all hotkey listeners');
		// remove unnecessary close contextmenu listeners
        window.removeEventListener('click', closeContextMenu, false);
        document.getElementById('toolbar').removeEventListener('mouseenter', closeContextMenu, false);

    	Waze.selectionManager.events.unregister("selectionchanged", null, setupSegmentContextMenu);

        // close the menu
        document.getElementById('cmContextMenu').style.display = 'none';
        menuResetEvent_SL = true;
    } catch (err) {}
};

var addSpecialMenuListeners = function () {
	cmlog([1,1], 'addSpecialMenuListeners()');
    if (document.getElementById('cmPinMenu').value) {
        window.removeEventListener('click', closeContextMenu, false);
        document.getElementById('toolbar').removeEventListener('mouseenter', closeContextMenu, false);
    } else {
        window.addEventListener('keydown', menuShortcutKeys, true);
        cmlog([2,2],'Adding just global hotkey listener via addSpecialMenuListeners()');
        window.addEventListener('click', closeContextMenu, false);
        document.getElementById('toolbar').addEventListener('mouseenter', closeContextMenu, false);
    }
};

var adjustContextMenubar = function(pos) {
	if (pos === 1) {
       	document.getElementById('cmFooterCaret').classList.add('fa-angle-down');
        document.getElementById('cmFooterCaret').classList.remove('fa-angle-up');
        document.getElementById('cmFooter').classList.add('cm-top');
        document.getElementById('cmFooter').classList.remove('cm-bottom');
        document.getElementById('cmFooter').style.marginBottom = '-1px';
        document.getElementById('cmContextMenu').insertBefore(document.getElementById('cmFooter'), document.getElementById('cmContextMenu').children[0]);
	} else {
        document.getElementById('cmFooterCaret').classList.add('fa-angle-up');
        document.getElementById('cmFooterCaret').classList.remove('fa-angle-down');
        document.getElementById('cmFooter').classList.remove('cm-top');
        document.getElementById('cmFooter').classList.add('cm-bottom');
        document.getElementById('cmFooter').style.marginBottom = '0';
        document.getElementById('cmContextMenu').appendChild(document.getElementById('cmFooter'));
	}
};

var hideMenuSection = function(evt, sectionName) {
    var that;
    if (evt) that = evt.target;
    else if (sectionName) that = document.querySelector('.cm-menu-section[name=' + sectionName + ']>.cm-hide');

    that.classList.toggle('fa-caret-down');
    that.classList.toggle('fa-caret-up');

    if (that.classList.contains('fa-caret-up')) { //hidden section
        that.parentNode.classList.add('cm-hidden');
        contextMenuSettings.hidden[that.parentNode.getAttribute('name')] = true;
    } else { //revealed section
        that.parentNode.classList.remove('cm-hidden');
        contextMenuSettings.hidden[that.parentNode.getAttribute('name')] = false;
    }
    localStorage.contextMenuSettings = JSON.stringify(contextMenuSettings);
};

var resetContextMenu = function (contextMenuSettings) {
	cmlog([1,1], 'resetContextMenu()');

    var menuHTML;

    if (menuResetEvent_RSel) document.getElementById('btnRSClear').click();
    menuResetEvent_RSel = false;

    try {
        document.querySelector('#user-tabs a[href="#sidepanel-roadselector"]').parentNode.id = 'tabRSel';
    } catch (err) {}
    // <label for="cm_any" class="btn cm-rsel-options badge cm-badge-right active"><input id="cm_any" type="radio" name="cmOptions" value=2 style="opacity: 0" checked>any</label> //dl - background-color: rgba(177, 210, 220, 0.75);

    if (contextMenuSettings.clipboard === 1) { //RSel
	    document.getElementById('cmContainer').innerHTML = `
	    <div id="cmRSelAutoAdd" class="cm-menu-section cm-rsel">
	        <dl><dt>RSel Auto-Add Names</dt>
	            <dd><div class="btn-group pull-left" data-toggle="buttons">
	                    <label for="cm_pri" class="btn cm-rsel-options cm-badge-left active"><input id="cm_pri" type="checkbox" value=1 checked>&nbsp;Primary</label>
	                    <label for="cm_alt" class="btn cm-rsel-options cm-badge-right active"><input id="cm_alt" type="checkbox" value=2 checked>Alternate&nbsp;</label>
	                </div>
	                <div class="pull-left btn-group" style="margin: 0px 2px">
	                    <button type="button" id="cmRoadType" class="btn cm-rsel-options active">Type</button>
	                </div>
	                <div class="btn-group pull-right">
	                    <button type="button" id="cmOpAddOr" class="btn cm-rsel-options cm-badge-left and" value="0" style="padding-right: 4px;">and</button>
	                    <button type="button" id="cmOpNot" class="btn cm-rsel-options cm-badge-right" style="padding-left: 4px;">!</button>
	            </div></dd>
	            <dd id="cm_SC">All <span id="cm_textSC">Primary/Alt. Street, City</span></dd>
	            <dd id="cm_S">All <span id="cm_textS">Primary/Alt. Street</span></dd>
	        </dl>
	    </div>`;
	} else {
		document.getElementById('cmContainer').innerHTML = '';
	}

    menuHTML = `
    <div class="cm-menu-header">
        <dl><dt id="cmMenuHeaderTitle">Copy To Clipboard</dt></dl>
    </div>
    <div id="cmMenuNoContent" class="cm-menu-section" style="display: none;"><dd>No valid segment(s) selected</dd></div>
    <div id="cmMenuContent">
        <div id="cm_street" name="street" class="cm-menu-section">
            <span class="fa fa-caret-down cm-hide"></span><span class="fa fa-caret-up cm-hide"></span>
            <dl id="cm_primaryStreet">
                <dt>Street</dt>
            </dl>
            <dl id="cm_altStreets">
                <dt>Alt street</dt>
            </dl>
        </div>
        <div id="cm_city" name="city" class="cm-menu-section">
            <span class="fa fa-caret-down cm-hide"></span><span class="fa fa-caret-up cm-hide"></span>
            <dl id="cm_primaryCity">
                <dt>City</dt>
            </dl>
            <dl id="cm_altCities">
                <dt>Alt city</dt>
            </dl>
        </div>
        <div name="state" class="cm-menu-section">
            <span class="fa fa-caret-down cm-hide"></span><span class="fa fa-caret-up cm-hide"></span>
            <dl id="cm_state">
                <dt>State</dt>
            </dl>
        </div>
        <div name="roadtype" class="cm-menu-section">
            <span class="fa fa-caret-down cm-hide"></span><span class="fa fa-caret-up cm-hide"></span>
            <dl id="cm_roadType">
                <dt>Road type</dt>
            </dl>
        </div>
        <div name="by" class="cm-menu-section">
            <span class="fa fa-caret-down cm-hide"></span><span class="fa fa-caret-up cm-hide"></span>
            <dl id="cm_updatedBy">
                <dt>Updated by</dt>
            </dl>
            <dl id="cm_createdBy">
                <dt>Created by</dt>
            </dl>
        </div>
        <div name="id" class="cm-menu-section">
            <dl>
                <dd id="cm_ids">Segment IDs</dd>
            </dl>
    </div></div>`;



    for (var h in contextMenuSettings.hidden) {
        if (contextMenuSettings.hidden[h]) //hideMenuSection(null, h);
            menuHTML = menuHTML.replace(new RegExp('(name="' + h + '" class=")','m'),'$1cm-hidden ');
    }

    document.getElementById('cmContainer').innerHTML += menuHTML;

    $('.cm-hide').click(hideMenuSection);

    // if menu is not pinned
    if (!document.getElementById('cmPinMenu').value) {
        setTimeout(addSpecialMenuListeners, 250);
    }

    adjustContextMenubar(contextMenuSettings.position);
};

var hidePasteMenu = function(bit) {
    if (bit === undefined) bit = !document.getElementById('cmClipboard').value;

    if ($('.cm-paste').length) {
        $('.cm-paste').each(function(i, node) {
            if (bit) node.style.display = 'none';
            else node.style.display = '';
        });
    }
};

var populateCopyMenu = function (segInfo, contextMenuSettings) {
	cmlog([1,1], 'populateCopyMenu()');

    document.getElementById('cmMenuNoContent').style.display = 'none';
    document.getElementById('cmMenuContent').style.display = 'block';

    try {
        resetContextMenu(contextMenuSettings);
        var updateNames = Object.keys(segInfo.ids),
            numNames = updateNames.length,
            n, selOption, emptyArr, s_names = {},
            s_ids = {}, clipboard, pasteOption, sectionElement;

        for (n = numNames; n--;) {
            sectionElement = document.getElementById('cm_' + updateNames[n]);
            if (sectionElement &&
                segInfo.ids[updateNames[n]] &&
                segInfo.ids[updateNames[n]].length) {

                if (updateNames[n] !== 'ids') {
                    emptyArr = 0;
                    s_names[updateNames[n]] = getUnique(segInfo.names[updateNames[n]])
                    s_ids[updateNames[n]] = getUnique(segInfo.ids[updateNames[n]])

                    for (var a = 0, aLength = s_ids[updateNames[n]].length; a < aLength; a++) {
                    	try {
	                    	selOption = document.createElement('dd');
	                        if (s_names[updateNames[n]][a]) {
	                            if (s_names[updateNames[n]][a] === '') { //no city or no street
	                                if (/city/i.test(updateNames[n])) {
	                                    selOption.innerHTML = 'No City';
	                                } else if (/street/i.test(updateNames[n])) {
	                                    selOption.innerHTML = 'No Street';
	                                }
	                                //selOption.name = '';
	                            } else { //add the property value to the menu
	                                selOption.innerHTML = s_names[updateNames[n]][a];
	                                //selOption.name = s_names[updateNames[n]][a];
	                            }
	                            sectionElement.appendChild(selOption);

	                            switch (updateNames[n]) {
	                                case 'roadType':
	                                    selOption.name = s_ids[updateNames[n]][a];
	                                    if (document.getElementById('cmClipboard').value) selOption.name = s_names[updateNames[n]][a];
	                                    selOption.onclick = function (e) {
	                                        //if (document.getElementById('cmPinMenu').value) e.stopPropagation();
	                                        copyTo(e, this.parentNode.id, this.name);
	                                    };
	                                    break;
	                                default:
	                                    selOption.name = s_names[updateNames[n]][a];
	                                    selOption.onclick = function (e) {
	                                        //if (document.getElementById('cmPinMenu').value) e.stopPropagation();
	                                        copyTo(e, this.parentNode.id, this.name);
	                                    };
	                            }
	                        } else {
	                            emptyArr++;
	                        }
	                    } catch(err) { console.error(err); }
                    }

                    // Check if something has been copied into clipboard for pasting...
                    if (document.getElementById('cmClipboard').value) addPasteItems('cm_' + updateNames[n]);

                    // Hide section if nothing to copy
                    if (emptyArr === aLength && !$('#input_cm_' + updateNames[n]).length) {
                        sectionElement.style.display = "none";
                        //sectionElement.parentNode.children[0].style.display = "none";
		            } else if ($('#input_cm_' + updateNames[n]).length) { //show if something to paste
		            	selOption = document.createElement('dd');
		            	selOption.innerHTML = '&nbsp;';
		            	sectionElement.appendChild(selOption);
		            }
                } else { // Segment IDs
                    selOption = document.getElementById('cm_ids');
                    selOption.name = segInfo.ids[updateNames[n]].join(',');
                    selOption.onclick = function (e) {
                        //if (document.getElementById('cmPinMenu').value) e.stopPropagation();
                        copyTo(e, this.id, this.name);
                    };
                }
                //--------------------------------------------
            } else if (sectionElement && !$('#input_cm_' + updateNames[n]).length) { // Hide section if nothing to copy
                //sectionElement.parentNode.style.display = "none";
                sectionElement.style.display = "none";
                sectionElement.parentNode.children[0].style.display = "none"; //caret-down
                sectionElement.parentNode.children[1].style.display = "none"; //caret-up
            } else if (sectionElement && $('#input_cm_' + updateNames[n]).length) { //show if something to paste
            	selOption = document.createElement('dd');
                if (/city/i.test(updateNames[n])) {
                    selOption.innerHTML = 'No City';
                } else if (/street/i.test(updateNames[n])) {
                    selOption.innerHTML = 'No Street';
                } /*else {
                	selOption.innerHTML = '&nbsp;';
                }*/
                if (selOption.innerHTML.length) {
	                selOption.name = '';
	                sectionElement.appendChild(selOption);
	                selOption.onclick = function (e) {
	                    //if (document.getElementById('cmPinMenu').value) e.stopPropagation();
	                    copyTo(e, this.parentNode.id, this.name);
	                };
	            	sectionElement.appendChild(selOption);
	            }
            }
        }
        // Hide sections if nothing to copy
        if (!s_names.primaryStreet.length && !s_names.altStreets.length && !$('#input_cm_primaryStreet').length) {
            document.getElementById('cm_primaryStreet').parentNode.style.display = 'none';
        }
        if (!s_names.primaryCity.length && !s_names.altCities.length && !$('#input_cm_primaryCity').length) {
            document.getElementById('cm_primaryCity').parentNode.style.display = 'none';
        }
        if (!s_names.state.length) document.getElementById('cm_state').parentNode.style.display = 'none';

        var numSegments = Waze.selectionManager.selectionCountByType.segment;
        if (!numSegments) numSegments = '0 Segment IDs';
        else if (numSegments === 1) numSegments = '1 Segment ID';
        else numSegments = numSegments + ' Segment IDs';
        document.getElementById('cm_ids').innerHTML = numSegments;

        //=================================================================================
        // RSEL-specific menu items
        //=================================================================================
        if (document.getElementById('cmRSel').value) {
            document.getElementById('cmMenuHeaderTitle').innerHTML = 'Send To Road Selector';

            var copyToRSelAndSelect = document.createElement('dd');
            copyToRSelAndSelect.id = 'cm_SCgo';
            copyToRSelAndSelect.zIndex = 1;
            copyToRSelAndSelect.className = 'fa fa-fast-forward pull-right cm-rsel-goselect';
            document.getElementById('cm_SC').parentNode.insertBefore(copyToRSelAndSelect, document.getElementById('cm_SC'));

            var copyToRSelAndSelect2 = copyToRSelAndSelect.cloneNode();
            copyToRSelAndSelect2.id = 'cm_Sgo';
            copyToRSelAndSelect.zIndex = 1;
            document.getElementById('cm_S').parentNode.insertBefore(copyToRSelAndSelect2, document.getElementById('cm_S'));

            document.getElementById('cmRSelAutoAdd').style.display = 'block';

            //name options
            document.getElementById('cm_pri').parentNode.addEventListener('click', function (e) {
                e.stopPropagation();
                var numBtnDown = this.parentNode.getElementsByClassName('active').length;
                if (this.children[0].checked) { //currently checked... decide whether to uncheck
                    this.classList.remove('active');
                    this.children[0].checked = false;
                    if (numBtnDown === 0) {
                        document.getElementById('cm_alt').parentNode.classList.add('active')
                        document.getElementById('cm_alt').checked = true;
                    }
                    document.getElementById('cm_textSC').innerHTML = 'Alt. Street, City';
                    document.getElementById('cm_textS').innerHTML = 'Alt. Street';
                } else { //unchecked
                    this.classList.add('active');
                    this.children[0].checked = true;
                    document.getElementById('cm_textSC').innerHTML = 'Primary/Alt. Street, City';
                    document.getElementById('cm_textS').innerHTML = 'Primary/Alt. Street';
                }
            }, false);
            document.getElementById('cm_alt').parentNode.addEventListener('click', function (e) {
                e.stopPropagation();
                var numBtnDown = this.parentNode.getElementsByClassName('active').length;
                if (this.children[0].checked) { //currently checked... decide whether to uncheck
                    this.classList.remove('active');
                    this.children[0].checked = false;
                    if (numBtnDown === 0) {
                        document.getElementById('cm_pri').parentNode.classList.add('active');
                        document.getElementById('cm_pri').checked = true;
                    }
                    document.getElementById('cm_textSC').innerHTML = 'Primary Street, City';
                    document.getElementById('cm_textS').innerHTML = 'Primary Street';
                } else { //unchecked
                    this.classList.add('active');
                    this.children[0].checked = true;
                    document.getElementById('cm_textSC').innerHTML = 'Primary/Alt. Street, City';
                    document.getElementById('cm_textS').innerHTML = 'Primary/Alt. Street';
                }
            }, false);

            //road type
            document.getElementById('cmRoadType').addEventListener('click', function (e) {
                e.stopPropagation();
                if (document.getElementById('cmRoadType').classList.contains('active')) {
                    document.getElementById('cmRoadType').classList.remove('active')
                } else {
                    document.getElementById('cmRoadType').classList.add('active')
                }
            }, false);

            //operations
            document.getElementById('cmOpAddOr').addEventListener('click', function (e) {
                e.stopPropagation();
                var opToggleLabel = ['and', 'or'],
                    newValue = this.value ^ 1;
                this.innerHTML = opToggleLabel[newValue];
                this.value = newValue;
                //console.info(this.value);
            }, false);

            document.getElementById('cmOpNot').addEventListener('click', function (e) {
                e.stopPropagation();
                if (document.getElementById('cmOpNot').classList.contains('active')) {
                    document.getElementById('cmOpNot').classList.remove('active')
                } else {
                    document.getElementById('cmOpNot').classList.add('active')
                }
            }, false);

            //menu selections
            document.getElementById('cm_SC').addEventListener('click', function (e) {
                copyTo(e, getAutoAddToRSelCase('cm_SC'), segInfo.ids);
            }, false);
            document.getElementById('cm_S').addEventListener('click', function (e) {
                copyTo(e, getAutoAddToRSelCase('cm_S'), segInfo.names);
            }, false);

            document.getElementById('cm_SCgo').addEventListener('click', function (e) {
                menuResetEvent_RSel = true;
                copyTo(e, getAutoAddToRSelCase('cm_SC'), segInfo.ids);
                document.getElementById('btnRSSelect').click();
            }, false);
            document.getElementById('cm_Sgo').addEventListener('click', function (e) {
                menuResetEvent_RSel = true;
                copyTo(e, getAutoAddToRSelCase('cm_S'), segInfo.names);
                document.getElementById('btnRSSelect').click();
            }, false);

        }
    } catch (err) {
    	cmlog([1,1], err);
        //console.error(err);
    };
};

//===============================================================================================

var SL_checkCountry = function () {
	cmlog([1,1], 'SL_checkCountry()');
    // returns a boolean value for menuResetEvent_SL and implies whether to run
    // reduceSpeedhelperOverhead() again

    //for (i=1; i<17; i++) {kph=Math.round(i*5*SL_imperial.mph2kph); mph=Math.round(kph*SL_imperial.kph2mph); console.info('kph:',kph,'---','mph:',mph)}
    if (!contextMenuSettings.countries) contextMenuSettings.countries = [];

    var savedNumCountries = contextMenuSettings.countries.length,
        currentNumCountries, currentCountries, convertUnits = false, isImperialCountry = false;

    try {
        currentCountries = [Waze.model.countries.top.abbr];
        currentNumCountries = 1;
    } catch (err) {
        console.warning('WMECM:', 'Could not find W.model.countries.top. Trying to use W.model.countries.objects instead.');
        try {
            currentCountries = Object.keys(Waze.model.countries.objects);
            currentNumCountries = currentCountries.length;
        } catch (err) {
            console.warning('WMECM:', 'WME objects might have been changed by Waze. This could be a problem and should be examined.')
            currentCountries = false;
            currentNumCountries = 0;
        }
    }

    var matchCount = 0;
    for (var c = 0; c < currentNumCountries; c++) {
        for (var s = 0; s < savedNumCountries; s++) {
            if (currentCountries[c] === contextMenuSettings.countries[s]) matchCount++;
        }
    }

    for (var ic = 0, icLength = SL_imperial.countries.length; ic < icLength; ic++) {
        if (currentCountries[0] === SL_imperial.countries[ic]) {
            isImperialCountry = true;
            break;
        }
    }

    if (isImperialCountry  && !Waze.prefs.attributes.isImperial) convertUnits = 1; //convert metric --> imperial
    else if (!isImperialCountry  && Waze.prefs.attributes.isImperial) convertUnits = 2; //convert imperial --> metric

    if (matchCount !== currentNumCountries) {
        contextMenuSettings.countries = currentCountries;
        localStorage.contextMenuSettings = JSON.stringify(contextMenuSettings);
        return {
            menuResetEvent_SL: true, //saved for later for implementing a check without having to refresh browser
            convertUnits: convertUnits
        };
    } else {
        return {
            menuResetEvent_SL: false, //saved for later for implementing a check without having to refresh browser
            convertUnits: convertUnits
        };
    }
};

var highlightSpeedSigns = function () {
	cmlog([1,1], 'highlightSpeedSigns()');
    //$('#signsholder_cm>div').removeAttr('style'); //reset styles
    $('#signsholder_cm>div:not(#btnCMclearSLs)').removeAttr('class'); //reset classes

    if (SL_imperial.convertUnits === null) {
        var countryResults = SL_checkCountry();
        SL_imperial.convertUnits = countryResults.convertUnits;
        SL = SL_imperial //debug
    }

    // Highlight the current speed limit
    var fwdSL = document.querySelector('input[name="fwdMaxSpeed"]'), fwdSpeedVal,
        revSL = document.querySelector('input[name="revMaxSpeed"]'), revSpeedVal,
        numSegments = Waze.selectionManager.selectionCountByType.segment,
        fwdSLMenu = document.querySelector('input[name="fwdMaxSpeed_cm'),
		revSLMenu = document.querySelector('input[name="revMaxSpeed_cm'),
		unverFwdChkBox = document.getElementById('fwdMaxSpeedUnverifiedCheckbox'),
		unverRevChkBox = document.getElementById('revMaxSpeedUnverifiedCheckbox'),
		signFwd, signRev;

	if (fwdSL !== null) fwdSpeedVal = fwdSL.valueAsNumber;
	if (revSL !== null) revSpeedVal = revSL.valueAsNumber;

    if ((fwdSpeedVal+revSpeedVal) === 0) {
    	return;
    } else if ((revSLMenu && revSLMenu.parentNode.style.display === 'none') || revSpeedVal===0) {
		revSpeedVal = false;
	} else if ((fwdSLMenu && fwdSLMenu.parentNode.style.display === 'none') || fwdSpeedVal===0) {
		fwdSpeedVal = false;
	}

    if (SL_imperial.convertUnits === 1) {
        if (fwdSpeedVal) fwdSpeedVal = Math.round(fwdSpeedVal * SL_imperial.kph2mph);
        if (revSpeedVal) revSpeedVal = Math.round(revSpeedVal * SL_imperial.kph2mph);
    } else if (SL_imperial.convertUnits === 2) {
        if (fwdSpeedVal) fwdSpeedVal = Math.round(fwdSpeedVal * SL_imperial.mph2kph);
        if (revSpeedVal) revSpeedVal = Math.round(revSpeedVal * SL_imperial.mph2kph);
	}

	signFwd = document.querySelector('#signsholder_cm>div[id="sign' + fwdSpeedVal + '"]');
	signRev = document.querySelector('#signsholder_cm>div[id="sign' + revSpeedVal + '"]');

    if ((fwdSpeedVal && revSpeedVal) && (fwdSpeedVal === revSpeedVal)) {
        if ((unverFwdChkBox === null && unverRevChkBox === null) || // if both have been verified
            (unverFwdChkBox && unverRevChkBox && unverFwdChkBox.checked === true && unverRevChkBox.checked === true)) {
            if (signFwd) signFwd.className ='cm-sl-verified cm-both';
        } else if ((unverFwdChkBox === null || unverFwdChkBox.checked === true) ||
        	(unverRevChkBox === null || unverRevChkBox.checked === true)) { // only one has been verified
            if (signFwd) signFwd.className ='cm-sl-unverified cm-one';
        } else if (unverFwdChkBox.checked === false && unverRevChkBox.checked === false) { // neither have been verified
            if (signFwd) signFwd.className ='cm-sl-unverified cm-both';
        }
    } else if ((fwdSpeedVal && revSpeedVal) && (fwdSpeedVal !== revSpeedVal)) {
        if ((unverFwdChkBox === null && unverRevChkBox === null) || // if both have been verified -- no checkboxes
            (unverFwdChkBox && unverRevChkBox && unverFwdChkBox.checked === true && unverRevChkBox.checked === true)) { // or both checked
            if (signFwd) signFwd.className ='cm-sl-verified cm-a';
            signRev.className ='cm-sl-verified cm-b';
        } else if (unverFwdChkBox === null || unverFwdChkBox.checked === true) { //fwdSpeedVal checked or no checkbox
            if (signFwd) signFwd.className ='cm-sl-verified cm-a';
            if (signRev)signRev.className ='cm-sl-unverified cm-b';
        } else if (unverRevChkBox === null || unverRevChkBox.checked === true) { //revSpeedVal checked or no checkbox
            if (signFwd) signFwd.className ='cm-sl-unverified cm-a';
            if (signRev)signRev.className ='cm-sl-verified cm-b';
        } else if (unverFwdChkBox.checked === false && unverRevChkBox.checked === false) { //both unchecked
            if (signFwd) signFwd.className ='cm-sl-unverified cm-a';
            if (signRev)signRev.className ='cm-sl-unverified cm-b';
        }
    } else if (fwdSpeedVal && !revSpeedVal) { // no reverse speed has been inputted
        if (document.querySelector('input[name="revMaxSpeed"]') === null) { // if revSpeedVal input does not exist bc this is a oneway road
            if (unverFwdChkBox === null || unverFwdChkBox.checked === true) { // if fwdSpeedVal has been verified
                if (signFwd) signFwd.className ='cm-sl-verified';
            } else if (unverFwdChkBox.checked === false) { //unverified speed on oneway road
                if (signFwd) signFwd.className ='cm-sl-unverified';
            }
        } else { //revSpeedVal input box does exist but is empty
            if (unverFwdChkBox === null || unverFwdChkBox.checked === true) { // if fwdSpeedVal has been verified, then draw green box
                if (signFwd) signFwd.className ='cm-sl-verified cm-a';
            } else if (unverFwdChkBox.checked === false) { //unverified speed on oneway road
                if (signFwd) signFwd.className ='cm-sl-unverified cm-a';
            }
        }
    } else if (revSpeedVal && !fwdSpeedVal) {
        if (document.querySelector('input[name="fwdMaxSpeed"]') === null) { // if fwdSpeedVal input does not exist bc this is a oneway road
            if (unverRevChkBox === null || unverRevChkBox.checked === true) { //revSpeedVal has been verified
                if (signRev)signRev.className ='cm-sl-verified';
            } else if (unverRevChkBox.checked === false) { //unverified speed on oneway road
                if (signRev)signRev.className ='cm-sl-unverified';
            }
        } else { //fwdSpeedVal does exist, but has not been inputted
            if (unverRevChkBox === null || unverRevChkBox.checked === true) { //revSpeedVal has been verified
                signRev.className ='cm-sl-verified cm-b';
            } else if (unverRevChkBox.checked === false) { //unverified speed on oneway road
                if (signRev) signRev.className ='cm-sl-unverified cm-b';
            }
        }
    }

    if (numSegments > 1) $('#signsholder_cm>div.cm-sl-verified').addClass('cm-sl-multisegs');
};

//------------------------------------------------------------------------------
var reduceSpeedhelperOverhead = function () {
	cmlog([1,1], 'reduceSpeedhelperOverhead()');
    // returns the innerHTML for slimmed down cmMenuContent, to be assigned to speedhelperHTML
    if (document.getElementById('cmSpeedhelperCSS')) document.getElementById('cmSpeedhelperCSS').remove();

    var speedhelperCSSEl = document.createElement('style');
    speedhelperCSSEl.type = 'text/css';
    speedhelperCSSEl.id = 'cmSpeedhelperCSS';
    speedhelperCSSEl.innerHTML = '#signsholder_cm>div[id^="sign"], #btnCMclearSLs {margin-bottom: 1px; ' + document.getElementById('signsholder').children[0].getAttribute('style') + '}\n' +
        '#signsholder_cm>div[id^="sign"]>* {' + document.querySelector('#signsholder>div[id^="sign"]>*').getAttribute('style') + '}\n';

    document.body.appendChild(speedhelperCSSEl);

    $('#signsholder_cm>div[id^="sign"]:not([id="signsError"]').removeAttr('style');
    $('#signsholder_cm>div[id^="sign"]>div').removeAttr('style');

    return document.getElementById('cmMenuContent').innerHTML;
};
//===============================================================================
var addABSpeedHelper = function(speedVal) {
	var fwdSLMenu = document.querySelector('input[name="fwdMaxSpeed_cm'),
		fwdSL = document.querySelector('input[name="fwdMaxSpeed"]'),
		fwdChkBox = document.getElementById('fwdMaxSpeedUnverifiedCheckbox'),
		prevFwdSpeedVal = fwdSLMenu.value,
		numSegsSelected = Waze.selectionManager.selectionCountByType.segment,
		pauseTime = (numSegsSelected > 10) ? (50+numSegsSelected) : 0;

    cmlog([5,3],speedVal);
    if (fwdSL.disabled === false) {
    	document.getElementById('cmWaitCover').style.display = 'block';
		requestAnimationFrame(function(){
			var fwdChkBoxMenu = document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm');
	        if (fwdChkBoxMenu !== null) fwdChkBoxMenu.checked = true;
			fwdSLMenu.value = speedVal;
		});

    	setTimeout(function(){
	    	if (fwdChkBox !== null && speedVal === prevFwdSpeedVal) {
				fwdChkBox.checked = true;
				fwdChkBox.dispatchEvent(changeEvent);
			} else if (fwdSL !== null) {
	    		fwdSL.value = speedVal;
	    		fwdSL.dispatchEvent(changeEvent);
	    		//$(fwdSL).val(speedVal).change();
	    		if (fwdChkBox !== null) setTimeout(function(){fwdChkBox.checked = true;},20);
			}

            if (!document.getElementById('cmPinMenu').value) setTimeout(closeContextMenu, 150);
            else setTimeout(highlightSpeedSigns,50);

			document.getElementById('cmWaitCover').style.display = 'none';
		},pauseTime);
    }
};
var addBASpeedHelper = function(speedVal) {
	var revSLMenu = document.querySelector('input[name="revMaxSpeed_cm'),
		revSL = document.querySelector('input[name="revMaxSpeed"]'),
		revChkBox = document.getElementById('revMaxSpeedUnverifiedCheckbox'),
		prevRevSpeedVal = revSLMenu.value,
		numSegsSelected = Waze.selectionManager.selectionCountByType.segment,
		pauseTime = (numSegsSelected > 10) ? (50+numSegsSelected) : 0;

	cmlog([5,3],speedVal);
    if (revSL.disabled === false) {
    	document.getElementById('cmWaitCover').style.display = 'block';
		requestAnimationFrame(function(){
			var revChkBoxMenu = document.getElementById('revMaxSpeedUnverifiedCheckbox_cm');
			if (revChkBoxMenu !== null) revChkBoxMenu.checked = true;
			revSLMenu.value = speedVal;
		});

    	setTimeout(function(){
	    	if (revChkBox !== null && speedVal === prevRevSpeedVal) {
	    		revChkBox.checked = true;
	    		revChkBox.dispatchEvent(changeEvent);
	    	} else if (revSL !== null) {
	    		revSL.value = speedVal;
	    		revSL.dispatchEvent(changeEvent);
	    		//$(revSL).val(speedVal).change();
	    		if (revChkBox !== null) setTimeout(function(){revChkBox.checked = true;},20);
	    	}

            if (!document.getElementById('cmPinMenu').value) setTimeout(closeContextMenu, 150);
            else setTimeout(highlightSpeedSigns,50);

			document.getElementById('cmWaitCover').style.display = 'none';
		},pauseTime);
    }
};
var addBothSpeedHelper = function(speedVal) {
	var fwdSLMenu = document.querySelector('input[name="fwdMaxSpeed_cm'),
		revSLMenu = document.querySelector('input[name="revMaxSpeed_cm'),
		fwdSL = document.querySelector('input[name="fwdMaxSpeed"]'),
		revSL = document.querySelector('input[name="revMaxSpeed"]'),
		fwdChkBox = document.getElementById('fwdMaxSpeedUnverifiedCheckbox'),
		revChkBox = document.getElementById('revMaxSpeedUnverifiedCheckbox'),
		prevFwdSpeedVal, prevRevSpeedVal,
		numSegsSelected = Waze.selectionManager.selectionCountByType.segment,
		pauseTime = (numSegsSelected > 10) ? 60 : 0;

	cmlog([5,3],speedVal);
	if ((revSLMenu && revSLMenu.parentNode.style.display === 'none') || revSLMenu === null) {
		addABSpeedHelper(speedVal);
	} else if ((fwdSLMenu && fwdSLMenu.parentNode.style.display === 'none') || fwdSLMenu === null) {
		addBASpeedHelper(speedVal);
	} else if (fwdSL.disabled === false && revSL.disabled === false) {
    	document.getElementById('cmWaitCover').style.display = 'block';
		prevFwdSpeedVal = fwdSLMenu.value;
		prevRevSpeedVal = revSLMenu.value;

		requestAnimationFrame(function(){
			var fwdChkBoxMenu = document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm'),
				revChkBoxMenu = document.getElementById('revMaxSpeedUnverifiedCheckbox_cm');
	        if (fwdChkBoxMenu !== null) fwdChkBoxMenu.checked = true;
			if (revChkBoxMenu !== null) revChkBoxMenu.checked = true;
	    	fwdSLMenu.value = speedVal; //$('#cmSpeedLimit input[type="number"]').val(speedVal);
	    	revSLMenu.value = speedVal;
		});

    	setTimeout(function(){
	    	if (fwdChkBox !== null && speedVal === prevFwdSpeedVal) {
				fwdChkBox.checked = true;
				fwdChkBox.dispatchEvent(changeEvent);
				//$('#fwdMaxSpeedUnverifiedCheckbox').prop('checked', true).change();
			} else if (fwdSL !== null) {
	    		fwdSL.value = speedVal;
	    		fwdSL.dispatchEvent(changeEvent);
				//$(fwdSL).val(speedVal).change();
			}
			//setTimeout(function(){
		    	if (revChkBox !== null && speedVal === prevRevSpeedVal) {
		    		revChkBox.checked = true;
		    		revChkBox.dispatchEvent(changeEvent);
	    			//$('#revMaxSpeedUnverifiedCheckbox').prop('checked', true).change();
		    	} else if (revSL !== null) {
	    			//revSL.value = speedVal;
	    			//revSL.dispatchEvent(changeEvent);
		    		$('input[name="revMaxSpeed"]').val(speedVal).change();
		    		if (fwdChkBox !== null) setTimeout(function(){fwdChkBox.checked = true;},20);
		    		if (revChkBox !== null) setTimeout(function(){revChkBox.checked = true;},40);
		    	}
			//}, 50);

            if (!document.getElementById('cmPinMenu').value) setTimeout(closeContextMenu, 150);
        	else setTimeout(highlightSpeedSigns,50);

			/*setTimeout(function(){
				try { //do it again just in case... sometimes necessary...
	        		$('input[name="revMaxSpeed"]').val(speedVal).change();
				} catch(err) {}
			}, 150);*/
			document.getElementById('cmWaitCover').style.display = 'none';
		}, pauseTime);
    }
};

var checkUnits = function(speedVal) {
    if (SL_imperial.convertUnits === 1) {
        return Math.round(speedVal * SL_imperial.mph2kph);
    } else if (SL_imperial.convertUnits === 2) {
        return Math.round(speedVal * SL_imperial.kph2mph);
    } else {
        return speedVal*1;
    }
};

var populateSpeedMenu = function (contextMenuSettings, nodeLabel) {
	cmlog([1,1], 'populateSpeedMenu(contextMenuSettings, ' + nodeLabel + ')');
    document.getElementById('cmMenuNoContent').style.display = 'none';
    document.getElementById('cmMenuContent').style.display = 'block';

    var slCurrentElementStatus = [!!document.getElementById('fwdMaxSpeedUnverifiedCheckbox'),
        !!document.getElementById('revMaxSpeedUnverifiedCheckbox'),
        !!document.getElementsByName('fwdMaxSpeed').length,
        !!document.getElementsByName('revMaxSpeed').length],
        newMenu = false,
        slMenuElementFlags = [!!document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm')+1,
        !!document.getElementById('revMaxSpeedUnverifiedCheckbox_cm')+1,
        document.getElementsByName('fwdMaxSpeed_cm').length+1,
        document.getElementsByName('revMaxSpeed_cm').length+1];

    if (slSavedMenuElementFlags === false) slSavedMenuElementFlags = slMenuElementFlags;


    for (var cc = 4, slNumSavedElementsMatched = 0, slNumElementsMatched = 0, slCurrentElementTotal=0; cc--;) {
        slNumElementsMatched += (((slMenuElementFlags[cc]*slCurrentElementStatus[cc]-1)>0)===slCurrentElementStatus[cc]);
        slNumSavedElementsMatched += (((slSavedMenuElementFlags[cc]*slCurrentElementStatus[cc]-1)>0)===slCurrentElementStatus[cc]);
        slCurrentElementTotal += slCurrentElementStatus[cc];
    }
    cmlog([4,3], 'slSavedMaxElementTotal', slSavedMaxElementTotal, '| slCurrentElementTotal', slCurrentElementTotal);
    cmlog([4,3], 'slNumSavedElementsMatched', slNumSavedElementsMatched, '| slNumElementsMatched', slNumElementsMatched);
    cmlog([4,3], 'slSavedMenuElementFlags', slSavedMenuElementFlags, '| slMenuElementFlags', slMenuElementFlags);

    if ( !speedhelperHTML || slNumElementsMatched !== 4 ||
    	(menuResetEvent_SL === true && slNumElementsMatched !== 4) ||
    	(slCurrentElementTotal === 4 && slSavedMaxElementTotal !== 4) ) {

        resetContextMenu(contextMenuSettings);
        document.getElementById('cmMenuHeaderTitle').innerHTML = 'Edit Speed Limits';

        if (speedhelperHTML && slNumSavedElementsMatched === 4) {
        	cmlog([4,1],'Replace - Copy Saved and Rebuild');
        	document.getElementById('cmMenuContent').innerHTML = speedhelperHTML;
        	menuResetEvent_SL = false;
        	newMenu = false;

        } else { //TODO: I think slNumElementsMatched !== 4 is redundant with slNumSavedElementsMatched
        	/*(document.getElementsByName('fwdMaxSpeed').length && !document.getElementsByName('fwdMaxSpeed_cm').length) ||
 			(document.getElementsByName('revMaxSpeed').length && !document.getElementsByName('revMaxSpeed_cm').length)) {*/
 			menuResetEvent_SL = true;
        	newMenu = true;
            try {
                /*var speedLimit = document.querySelector('#segment-edit-general div.speed-limit').cloneNode(true);
                if (speedLimit) {
	    	        cmlog([4,2],'Replace - Clone Source and Rebuild');
	                speedLimit.id = 'cmSpeedLimit';
	                speedLimit.className = 'cm-speed-limit cm-menu-section';
	                speedLimit.innerHTML = '<div id="signsContainer" style="min-height: ' + ((speedhelperHeight) ? (speedhelperHeight + 'px; ') : 'auto; ') + 'margin-bottom: 15px; opacity: 0.9;"></div><div class="form-inline">' + speedLimit.innerHTML + '</div>';
	                document.getElementById('cmMenuContent').innerHTML = '';
	                document.getElementById('cmMenuContent').appendChild(speedLimit);

	                document.querySelector('#cmSpeedLimit #signsholder').id = 'signsholder_cm';
	    	        document.getElementById('signsContainer').appendChild(document.getElementById('signsholder_cm')); // Move the signs to its own container
	    	    } else {*/
	    	    	cmlog([4,1],'Replace - Overwrite with Source and Rebuild');
	    	    	document.getElementById('cmMenuContent').innerHTML = document.querySelector('#segment-edit-general div.speed-limit').outerHTML;
	    	    	speedLimit = document.getElementById('cmMenuContent').children[0];
	                speedLimit.innerHTML = '<div id="signsContainer" style="min-height: ' + ((speedhelperHeight) ? (speedhelperHeight + 'px; ') : 'auto; ') + 'margin-bottom: 15px; opacity: 0.9;"></div><div class="form-inline">' + speedLimit.innerHTML + '</div>';
	                speedLimit.id = 'cmSpeedLimit';
	                speedLimit.className = 'cm-speed-limit cm-menu-section';

	                document.querySelector('#cmSpeedLimit #signsholder').id = 'signsholder_cm';
	    	        document.getElementById('signsContainer').appendChild(document.getElementById('signsholder_cm')); // Move the signs to its own container
	    	    //}
            } catch (err) {
            	cmlog([4,2],'Replace - Caught. No SpeedHelper or SpeedHelper is not ready.', err);
            }
        }

        //--------------------------------------------------------------------------
        try {
            var wazeVerifyChkBoxLabel = document.querySelectorAll('#segment-edit-general div.speed-limit input[type="checkbox"]+label'),
                wazeChkbox, wazeChkboxSelector;
            // Add event listeners for verified SL checkbox in menu
            for (var cb = 0; cb < wazeVerifyChkBoxLabel.length; cb++) {
                wazeChkbox = wazeVerifyChkBoxLabel[cb].parentNode.children[0];
                cmChkboxSelector = '#cmSpeedLimit ' + '#' + wazeChkbox.id;

                if (menuResetEvent_SL) document.querySelector(cmChkboxSelector).id = wazeChkbox.id + '_cm';

                document.querySelector(cmChkboxSelector + '_cm').addEventListener('click', function(e) {
                    e.stopPropagation();
                    this.parentNode.children[0].checked = !document.getElementById(this.parentNode.children[0].id.slice(0, -3)).checked;
                }, false);
            }
        } catch (err) {cmlog([4,2], 'wazeVerifyChkBoxLabel', err);}

        //--------------------------------------------------------------------------
        try {
            var wazeSpeedInput = document.querySelectorAll('#segment-edit-general div.speed-limit input[type="number"]'),
                wazeSpeedInputLength = wazeSpeedInput.length,
                cmSpeedInput, cmSpeedInputSelector_orig, cmSpeedInputSelector, nm;

            for (nm = 0; nm < wazeSpeedInputLength; nm++) {
            	cmSpeedInputSelector_orig = '#cmSpeedLimit input[name="' + wazeSpeedInput[nm].name + '"]';
            	cmSpeedInputSelector = '#cmSpeedLimit input[name="' + wazeSpeedInput[nm].name + '_cm' + '"]';

                if (menuResetEvent_SL) {
                	document.querySelector(cmSpeedInputSelector_orig).setAttribute('name', wazeSpeedInput[nm].name + '_cm');
                }

                cmSpeedInput = document.querySelector(cmSpeedInputSelector);
                cmSpeedInput.addEventListener('click', function (e) {
                    e.stopPropagation();
                    this.select();
					document.getElementById('cmContextMenu').removeEventListener('mouseenter', addHotkeyListener, false);
                    window.removeEventListener('keydown', menuShortcutKeys, true);
					cmlog([2,2],'Editing speed value, so removing global hotkey listener');
                }, false);

                cmSpeedInput.addEventListener('blur', function (e) {
					document.getElementById('cmContextMenu').addEventListener('mouseenter', addHotkeyListener, false);
                    window.addEventListener('keydown', menuShortcutKeys, true);
                    cmlog([2,2],'Removed focus from speed input field, so adding global hotkey listener again');
                    $('input[name="' + this.name.slice(0, -3) + '"]').val(this.value).change();
                }, false);

                cmSpeedInput.addEventListener('change', function (e) {
                    //e.stopPropagation();
                    $('input[name="' + this.name.slice(0, -3) + '"]').val(this.value).change();
                }, false);
            }
        } catch (err) {cmlog([4,2], 'cmSpeedInput', err);}
		//--------------------------------------------------------------------------
        //--------------------------------------------------------------------------
        try { // Try adding some event listeners for closing the menu if not pinned:
            if (!document.getElementById('cmPinMenu').value) {
                document.getElementById('cmSpeedLimit').addEventListener(
                    'mouseleave',
                    function () {
                        cmlog([2,2], 'Adding event listners to allow menu to close');
                        addSpecialMenuListeners();
                        //$('input[name="fwdMaxSpeed_cm"]').val($('input[name="fwdMaxSpeed"]').val());
                        //$('input[name="revMaxSpeed_cm"]').val($('input[name="revMaxSpeed"]').val());
                    }, false);

                document.getElementById('cmSpeedLimit').addEventListener(
                    'mouseenter',
                    function () { //prevent menu from closing
                    	cmlog([2,2],'Preventing menu from closing by removing event listeners');
                        window.removeEventListener('click', closeContextMenu, false);
                        document.getElementById('toolbar').removeEventListener('mouseenter', closeContextMenu, false);
                    }, false);
            }
        } catch (err) { cmlog([4,2], 'addSpecialMenuListeners', err); }

        //--------------------------------------------------------------------------
        // Add event listener for the checkbox
        if (document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm') !== null) {
            document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm').parentNode.addEventListener('click', function(e) {
                this.children[0].checked = !this.children[0].checked;
            }, false);
        }
        if (document.getElementById('revMaxSpeedUnverifiedCheckbox_cm') !== null) {
            document.getElementById('revMaxSpeedUnverifiedCheckbox_cm').parentNode.addEventListener('click', function(e) {
                this.children[0].checked = !this.children[0].checked;
            }, false);
        }

        var fwdSpeedEl = document.querySelector('input[name="fwdMaxSpeed_cm"]');
        if (fwdSpeedEl) {
        	if (fwdSpeedEl.parentNode.querySelector('.fa-ban') === null) {
	            var clearSignFwd = document.createElement('span');
	            clearSignFwd.className = 'fa fa-ban';
            	fwdSpeedEl.parentNode.insertBefore(clearSignFwd, fwdSpeedEl.parentNode.children[0]);
            	clearSignFwd.addEventListener('click', function() {
                    requestAnimationFrame(function(){
		                addABSpeedHelper(0);
		            });
	            }, false);
            }
        }

       	var revSpeedEl = document.querySelector('input[name="revMaxSpeed_cm"]');
        if (revSpeedEl) {
        	if (revSpeedEl.parentNode.querySelector('.fa-ban') === null) {
	            var clearSignRev = document.createElement('span');
	            clearSignRev.className = 'fa fa-ban';
            	revSpeedEl.parentNode.insertBefore(clearSignRev, revSpeedEl.parentNode.children[0]);
            	clearSignRev.addEventListener('click', function() {
                    requestAnimationFrame(function(){
		                addBASpeedHelper(0);
		            });
	            }, false);
			}
        }
        //--------------------------------------------------------------------------
        var addListenersToSigns = function () {
	        // Add clear SL button:
	        if (document.getElementById('btnCMclearSLs') === null) {
				document.getElementById('signsholder_cm').innerHTML = '<div id="btnCMclearSLs" class="fa fa-ban"><div id="spd_0">0</div></div>' +
					document.getElementById('signsholder_cm').innerHTML;
	        }

            var cmSpeedSigns = document.getElementById('signsholder_cm').children;
            for (var ss = cmSpeedSigns.length; ss--;) {
                //--------------------------------------------------------------
                cmSpeedSigns[ss].addEventListener('click', function (e) {
                    e.preventDefault();
                    if (!this.classList.length || !this.classList.contains('cm-sl-verified')) {
                        var speedVal = this.firstElementChild.innerHTML;
                        speedVal = checkUnits(speedVal);
                    } else {
                        speedVal = 0;
                    }

                    if (e.shiftKey) { //AB - fwd
	                    requestAnimationFrame(function(){
	                        addABSpeedHelper(speedVal);
	                    });
                    } else if (e.ctrlKey || e.altKey || e.metaKey ) { //BA - rev
                        requestAnimationFrame(function(){
                        	addBASpeedHelper(speedVal);
                        });
                    } else {
                        requestAnimationFrame(function(){
                        	addBothSpeedHelper(speedVal);
                        });
                    }
                }, false);
                //--------------------------------------------------------------
                cmSpeedSigns[ss].addEventListener('mousedown', function (ev) {
                    //console.info(this);
                    this.draggable = true;
                    if (document.getElementById('cmPinMenu').value) {
                        document.getElementById('cmContextMenu').removeEventListener('dragstart', allowMenuDrag, false);
                        document.getElementById('cmContextMenu').draggable = false;
                        window.addEventListener('mouseup', resetDrag, false);
                    }
                }, false);

                cmSpeedSigns[ss].addEventListener('dragstart', function (ev) {
                    //console.info(this);
                    this.style.opacity = 0.3;
                    this.children[0].opacity = 0.7;
                    ev.dataTransfer.setData('text', this.children[0].innerHTML);
                    ev.dataTransfer.setDragImage(this, ev.offsetX, ev.offsetY);
                }, false);

                cmSpeedSigns[ss].addEventListener('dragend', function (ev) {
                    this.style.opacity = '';
                    this.children[0].opacity = '';
                    this.draggable = false;
                    resetDrag();
                    window.removeEventListener('mouseup', resetDrag, false);
                }, false);
            }

            // Add event listener for the checkbox
            if (document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm') !== null) {
                document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm').parentNode.addEventListener('click', function(e) {
                    this.children[0].checked = !this.children[0].checked;
                }, false);
            }
            if (document.getElementById('revMaxSpeedUnverifiedCheckbox_cm') !== null) {
                document.getElementById('revMaxSpeedUnverifiedCheckbox_cm').parentNode.addEventListener('click', function(e) {
                    this.children[0].checked = !this.children[0].checked;
                }, false);
            }

            var fwdSpeedEl = document.querySelector('input[name="fwdMaxSpeed_cm"]');
            if (fwdSpeedEl) {
                // Allow drop event
                fwdSpeedEl.addEventListener('dragover', function(ev) {
                    if (ev.preventDefault()) ev.preventDefault();
                    this.style.border = '1px solid cyan';
                    return false;
                }, false);
                fwdSpeedEl.addEventListener('dragleave', function(ev) {
                    this.style.border = '';
                }, false);
                // Drop event
                fwdSpeedEl.addEventListener('drop', function(ev) {
                    if (ev.preventDefault()) ev.preventDefault();
                    var speedVal = ev.dataTransfer.getData('text');
                    requestAnimationFrame(function(){
	                    addABSpeedHelper(speedVal);
	                });
                    this.style.border = '';
                }, false);
            }

           	var revSpeedEl = document.querySelector('input[name="revMaxSpeed_cm"]');
            if (revSpeedEl) {
                // Allow drop event
                revSpeedEl.addEventListener('dragover', function(ev) {
                    if (ev.preventDefault()) ev.preventDefault();
                    this.style.border = '1px solid cyan';
                    return false;
                }, false);
                revSpeedEl.addEventListener('dragleave', function(ev) {
                    this.style.border = '';
                }, false);
                // Drop event
                revSpeedEl.addEventListener('drop', function(ev) {
                    if (ev.preventDefault()) ev.preventDefault();
                    var speedVal = ev.dataTransfer.getData('text');
                    requestAnimationFrame(function(){
                    	addBASpeedHelper(speedVal);
                    });
                    this.style.border = '';
                }, false);
            }
            //-----------------------------------------------

		    slCurrentElementStatus = [!!document.getElementById('fwdMaxSpeedUnverifiedCheckbox'),
		        !!document.getElementById('revMaxSpeedUnverifiedCheckbox'),
		        !!document.getElementsByName('fwdMaxSpeed').length,
		        !!document.getElementsByName('revMaxSpeed').length],
		    slMenuElementFlags = [!!document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm')+1,
		        !!document.getElementById('revMaxSpeedUnverifiedCheckbox_cm')+1,
		        document.getElementsByName('fwdMaxSpeed_cm').length+1,
		        document.getElementsByName('revMaxSpeed_cm').length+1];

		    for (var cc = 4, slNumElementsMatched = 0, slCurrentElementTotal=0; cc--;) {
		        slCurrentElementTotal += slCurrentElementStatus[cc];
		    }

            if (slCurrentElementTotal >= slSavedMaxElementTotal) {
                speedhelperHTML = reduceSpeedhelperOverhead();
                slSavedMaxElementTotal = slCurrentElementTotal;
                slSavedMenuElementFlags = slMenuElementFlags;
                cmlog([4,4],'*** speedhelperHTML replaced ***')
            } else if (newMenu) {
            	reduceSpeedhelperOverhead();
            }
            highlightSpeedSigns(); // this is necessarily at the end to play catchup bc of the slower initialization times of Speedhelper elements
        };

        //--------------------------------------------------------------------------
        var waitCount = 0,
            maxWait = 30;

        var waitForSpeedhelper = function () {
            var signsHolder = document.querySelector('#segment-edit-general div.speed-limit #signsholder');
            if (signsHolder !== null && !document.getElementById('signsContainer').children.length) {
                menuResetEvent_SL = false;
                document.getElementById('signsContainer').innerHTML = signsHolder.outerHTML;
                document.querySelector('#signsContainer>#signsholder').id = 'signsholder_cm';
                speedhelperHeight = $('#signsholder_cm').height();

                addListenersToSigns();
            } else if (waitCount++ < maxWait) {
                setTimeout(waitForSpeedhelper, 30*waitCount);
            /*} else if ((slCurrentElementTotal > slSavedMaxElementTotal) && !speedhelperHTML) { //for speedhelperHTML...
                slSavedMaxElementTotal = slCurrentElementTotal;*/
            } else {
            	speedhelperHeight = false;
            }
            return;
        };
        //------------------------------------------------------
        setTimeout(function () {
            if (document.getElementById('signsContainer') && document.getElementById('signsContainer').children.length) {
                addListenersToSigns();
            } else if (speedhelperHeight !== false) {
                setTimeout(waitForSpeedhelper, 30);
            }
        }, 30); //timeout may be needed for countries with many speedlimit signs loaded by Speedhelper
        //------------------------------------------------------

    } // menuResetEvent_SL === true

    var $fwdSL = $('input[name="fwdMaxSpeed"]'), hasFwdSL = $fwdSL.length,
    	$revSL = $('input[name="revMaxSpeed"]'), hasRevSL = $revSL.length,
    	hasOnlyOneSL = !(hasFwdSL && hasRevSL),
    	fwdSLMenu = document.querySelector('input[name="fwdMaxSpeed_cm"]'),
    	revSLMenu = document.querySelector('input[name="revMaxSpeed_cm"]'),
    	fwdChkBox, revChkBox, fwdMenuChkBox, revMenuChkBox;

    //cmlog([5,3], 'hasFwdSL, hasRevSL, hasOnlyOneSL, nodeLabel');
    //cmlog([5,3], hasFwdSL, hasRevSL, hasOnlyOneSL, nodeLabel);
	if (hasFwdSL || hasRevSL) {
	    fwdMenuChkBox = document.getElementById('fwdMaxSpeedUnverifiedCheckbox_cm');
    	fwdChkBox = document.getElementById('fwdMaxSpeedUnverifiedCheckbox');
	    revMenuChkBox = document.getElementById('revMaxSpeedUnverifiedCheckbox_cm');
	    revChkBox = document.getElementById('revMaxSpeedUnverifiedCheckbox');

	    // Update values of the context menu
	    // Hide input fields that aren't relevant for selected segment(s)
	    if (fwdSLMenu !== null) {
			if (hasOnlyOneSL || nodeLabel !== 'B') {
			    if (hasFwdSL) {
					fwdSLMenu.value = $fwdSL.val();
			    	fwdSLMenu.parentNode.style.display = 'inline-block';

				    if (fwdMenuChkBox !== null) {
					    if (fwdChkBox !== null) {
					    	fwdMenuChkBox.checked = fwdChkBox.checked;
					    	fwdMenuChkBox.parentNode.style.visibility = 'visible';
					    	fwdMenuChkBox.parentNode.style.display = 'inline-block';
					    } else {
					    	fwdMenuChkBox.parentNode.style.visibility = 'hidden';
					    	fwdMenuChkBox.parentNode.style.display = 'inline-block';
					    }
					}
			    } else {
			    	fwdSLMenu.parentNode.style.display = 'none';
			    	if (fwdMenuChkBox !== null) fwdMenuChkBox.parentNode.style.display = 'none';
			    }
			} else if (fwdMenuChkBox !== null) {
				fwdSLMenu.parentNode.style.display = 'none';
		    	fwdMenuChkBox.parentNode.style.display = 'none';
		    }
		}

		if (revSLMenu !== null) {
		    if (hasOnlyOneSL || nodeLabel !== 'A') {
			    if (hasRevSL) {
			    	revSLMenu.value = $revSL.val();
			    	revSLMenu.parentNode.style.display = 'inline-block';

				    if (revMenuChkBox !== null) {
					    if (revChkBox !== null) {
					    	revMenuChkBox.checked = revChkBox.checked;
					    	revMenuChkBox.parentNode.style.visibility = 'visible';
					    	revMenuChkBox.parentNode.style.display = 'inline-block';
					    } else {
					    	revMenuChkBox.parentNode.style.visibility = 'hidden';
					    	revMenuChkBox.parentNode.style.display = 'inline-block';
					    }
				    }
			    } else {
			    	revSLMenu.parentNode.style.display = 'none';
			    	if (revMenuChkBox !== null) revMenuChkBox.parentNode.style.display = 'none';
			    }
			} else if (revMenuChkBox !== null) {
				revSLMenu.parentNode.style.display = 'none';
		    	revMenuChkBox.parentNode.style.display = 'none';
		    }
		}

	    highlightSpeedSigns();

    } else {
    	cmlog([4,2],'No SL. Hiding...');
        document.getElementById('cmMenuNoContent').style.display = 'block';
        document.getElementById('cmMenuContent').style.display = 'none';
	}
    //--------------------------------------------------------------------------

    if (menuResetEvent_SL) menuResetEvent_SL = false; //redundant, but just to make sure :)
};

//==============================================================================================
var selectedItemsIsSegment = function () {
    var sel = Waze.selectionManager.selectedItems, //returns empty array if nothing
        selLength = sel.length,
        s, segments = [];

	cmlog([1,1], 'selectedItemsIsSegment()');
    for (s = 0; s < selLength; s++) {
        if (sel[s].model.type === 'segment') segments.push(sel[s]);
    }

    return (segments.length) ? {nodeLabel: false, segments: segments} : false;
};
//----------------------------------------------------------------------------------------------
var selectionIsSegment = function (e) {
	cmlog([1,1], 'selectionIsSegment(e)')
   var sel, selLength, s, segments = [],
        numSelected, eventFeatures, evTarget, nodeLabel = false;

    // First check for segments under cursor (hover/mouseover)
    if (e && e.target) {
    	evTarget = $(e.target).get(0); //normalization by jQuery is necessary for FF compatibility
    } else {
    	evTarget = false;
    }

	//cmlog([1], evTarget._featureId, evTarget._geometryClass)
	if 	(evTarget && evTarget._featureId &&
    	(evTarget._geometryClass === "OpenLayers.Geometry.LineString" ||
         evTarget._geometryClass === "OpenLayers.Geometry.Point")) {

		e.preventDefault();

		if (evTarget._geometryClass === "OpenLayers.Geometry.Point" && evTarget._style) {
			if (evTarget._style.label === 'A') {
				nodeLabel = 'A';
			} else if (evTarget._style.label === 'B') {
				nodeLabel = 'B';
			}
		}

        sel = Waze.selectionManager.selectedItems; //returns empty array if nothing
        selLength = sel.length;

        eventFeatures = Waze.map.segmentLayer.getFeatureById(evTarget._featureId); //segment layer -- returns null if nothing
    	//cmlog([1,0], 'eventFeatures =')
    	//console.debug(eventFeatures);

        if (eventFeatures && eventFeatures.model.type === 'segment') {
            segments[0] = eventFeatures; //return result from W.map.segmentLayer.getFeatureById(._featureId) is the same as individual objects within the array returned by W.selectionManager.selectedItems
            try {
            	//cmlog([1,0],'SegID:',eventFeatures.model.attributes.id);
            	Waze.selectionManager.select([eventFeatures.model]); // [eventFeatures.model] is the same as the return result for one seg from W.model.segments.getByIds([id])
        	} catch(err) { cmlog([1,0], '<tantrum>'); console.error(err); }
        }

        //Now check for any selected segments... any duplicates of the hovered
        //segment will be dealt with in the next steps using object literals
        try {
	        for (s = 0; s < selLength; s++) {
	            if (sel[s].model.type === 'segment') segments.push(sel[s]);
	        }
	    } catch(err) {
			if (e.type === 'selectionchanged') {
		        sel = e.selected;
		        selLength = sel.length;

		        for (s = 0; s < selLength; s++) {
		            if (sel[s] && sel[s].model.type === 'segment') segments.push(sel[s]);
		        }
		    }
	    }
        //console.info(segments);

        document.getElementById('cmMenuNoContent').style.display = 'none';
        document.getElementById('cmMenuContent').style.display = 'block';

        return (segments.length) ? {nodeLabel: nodeLabel, segments: segments} : false; //no segments near cursor

    } else if (e.type === 'selectionchanged') {
        sel = e.selected;
        selLength = sel.length;

        for (s = 0; s < selLength; s++) {
            if (sel[s] && sel[s].model.type === 'segment') segments.push(sel[s]);
        }

        return (segments.length) ? {nodeLabel: false, segments: segments} : false; //no segments near cursor

    } else {
        return false;
    }
};

//----------------------------------------------------------------------------------------------

var setupSegmentContextMenu = function (e) {
	cmlog([1,0],'------------------------------------------------------------')
    cmlog([1,1], 'setupSegmentContextMenu()');
	if (document.getElementById('cmContextMenu') && document.getElementById('cmContextMenu').style.display !== 'none') {
		var selectedStuff = selectionIsSegment(e);

	    if (selectedStuff) {
	    	if (document.getElementById('cmRSel').value && document.getElementById('cmRSelAutoAdd'))
	    	    document.getElementById('cmRSelAutoAdd').style.display = 'block';

	        switch (contextMenuSettings.clipboard) {
	            case 2:
	                populateSpeedMenu(contextMenuSettings, selectedStuff.nodeLabel);
	                break;
	            case 1:
	            case 0:
	                var segInfo = getSegmentProperties(selectedStuff);
	                populateCopyMenu(segInfo, contextMenuSettings);
	                break;
	        }
	    } else if (document.getElementById('cmPinMenu').value) {
			if (document.getElementById('cmRSelAutoAdd'))
			    document.getElementById('cmRSelAutoAdd').style.display = 'none';
	        document.getElementById('cmMenuNoContent').style.display = 'block';
	        document.getElementById('cmMenuContent').style.display = 'none';
	        return false;
	    } else {
	    	cmlog([1,1],'No segment detected.')
	        return false;
	    }
	} else {
		return false;
	}
};

//=======================================================================================
//var pressedKeys = [];
var menuShortcutKeys = function (e) {
	cmlog([1,1], 'menuShortcutKeys()');
    switch (e.which) {
        case 49: //81: //q
            e.preventDefault();
            e.stopPropagation();
            document.getElementById('cmClipboard').click();
            break;
        case 50: //87: //w
            e.preventDefault();
            e.stopPropagation();
            document.getElementById('cmRSel').click();
            break;
        case 51: //69: //e
            e.preventDefault();
            e.stopPropagation();
            document.getElementById('cmSpeed').click();
            break;
        default:
            return false;
    }
};

//--------------------------------------------------------------------------------
var cursorOffsetX, cursorOffsetY;

var moveMenu = function (evt) {
    //cmlog([1],'moveMenu()');
	try {
	    evt.preventDefault();
	    evt.stopPropagation();
	    //evt.dataTransfer.effectAllowed = 'move';
	    requestAnimationFrame( function() {
		    document.getElementById('cmContextMenu').classList.add('cm-drag');
		    document.getElementById('cmContextMenu').style.top = evt.clientY - cursorOffsetY + 'px';
		    document.getElementById('cmContextMenu').style.left = evt.clientX - cursorOffsetX  + 'px';
		});
	} catch (err) { console.error(err); }
};

var placeMenu = function (endevt) {
	cmlog([1,1], 'placeMenu()');
    endevt.preventDefault();
    //endevt.dataTransfer.dropEffect = 'move';
	try {
	    //document.getElementById('cmContextMenu').style.display = 'block';
	    setTimeout(function(){document.getElementById('cmContextMenu').classList.remove('cm-drag')},50);
	    if (!isFirefox) {
	    	document.getElementById('cmContextMenu').style.display = 'block';
	    	document.getElementById('cmContextMenu').removeEventListener('drag', moveMenu, false);
	    } else {
	    	window.removeEventListener('mousemove', moveMenu, true);
			window.removeEventListener('mouseup', placeMenu, true);
	    }
	} catch (err) { console.error(err); }
};

var allowMenuDrag = function (startevt) {
	cmlog([1,1], 'allowMenuDrag()');
	//console.info(startevt);
	try {
	    if (document.getElementById('cmContextMenu').draggable) {
	    	document.getElementById('cmContextMenu').classList.add('cm-drag');
	    	if (!isFirefox) {
	    		setTimeout(function(){document.getElementById('cmContextMenu').style.display = 'none';},20);
	    	    cursorOffsetX = startevt.offsetX;
		        cursorOffsetY = startevt.offsetY;
		        document.getElementById('cmContextMenu').addEventListener('drag', moveMenu, false);
			} else {
				startevt.preventDefault();
				startevt.stopPropagation();
		        cursorOffsetX = startevt.layerX;
		        cursorOffsetY = startevt.layerY;
	            window.addEventListener('mousemove', moveMenu, true);
	            window.addEventListener('mouseup', placeMenu, true);
		    }
	    }
	} catch (err) { console.error(err); }
};

// Setup dragging for when menu is pinned to page
var dragMenuSetup = function(pinState) {
	if (pinState === undefined) pinState = document.getElementById('cmPinMenu').value;

	var contextMenu = document.getElementById('cmContextMenu'),
		mapDiv = document.getElementById('map');

    if (pinState && contextMenu.draggable === false) {
        contextMenu.draggable = true;
        contextMenu.addEventListener('dragstart', allowMenuDrag, false);

        if (!isFirefox) {
	        mapDiv.addEventListener('drop', placeMenu, false);
	        mapDiv.ondragover = function (e) {
	            e.preventDefault();
	            e.dataTransfer.effectAllowed = 'move';
	            e.dataTransfer.dropEffect = 'move';
	        };
        }
    } else if (!pinState && contextMenu.draggable === true) {
        contextMenu.draggable = false;
    	contextMenu.removeEventListener('dragstart', allowMenuDrag);
        if (!isFirefox) {
	        mapDiv.removeEventListener('drop', placeMenu);
	    }

    }
};

//for preventing conflicts with SL sign drags
var resetDrag = function() {
    window.removeEventListener('mouseup', resetDrag, false);
    dragMenuSetup();
};

//=======================================================================================
var showPopupPanel = function(updateVersion, updateText, forumURL) {
	var popPanelWidth = 600,
	 	popPanelCSS = document.createElement('style'),
	 	popPanelHTML = document.createElement('div');

	popPanelCSS.type = 'text/css';
	popPanelCSS.id = 'cssCMupdate';
 	popPanelCSS.innerHTML =
		'.cm-panel { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);' +
			' width: ' + popPanelWidth + 'px; padding: 10px 25px; margin: 0; overflow-y: auto; overflow-x: auto; word-wrap: break-word;' +
			' background-color: white; box-shadow: 0px 5px 20px #555555; border: 1px solid #858585; border-radius: 10px; }\n' +
		'.cm-panel .fa-exclamation-circle { margin: -5px 16px 10px 8px; line-height: .9; font-size: 56px;}\n' +
		'.cm-panel-inner { padding: 0px 5px; }\n' +
		'.cm-panel-section {display: block; font-size: 14px; margin-bottom: 10px; text-align: left; padding: 0px; }\n' +
		'.cm-panel h2  { margin-top: 15px; margin-left: 80px; font-size: 32px; font-weight: bold; text-align: left; color: #C0C0C0 }\n' +
		'.cm-panel-hr  { display: block; border: 0; height: 0; border-top: 1px solid rgba(0, 0, 0, 0.1);' +
			' border-bottom: 1px solid rgba(255, 255, 255, 0.3); margin-top: 8px; margin-bottom: 15px; }\n' +
		'.cm-panel .cm-btn-container { position: relative; display: table; margin: 0px auto 8px; vertical-align: middle; padding: 0}\n' +
		'.cm-panel .btn { margin: 0px 5px; padding: 0px 15px; display: inline-block; height: 32px; }\n';

	document.body.appendChild(popPanelCSS);

	popPanelHTML.id = 'divCMupdate';
	popPanelHTML.style.backgroundColor = 'rgba(0,0,0,0.5)';
	popPanelHTML.style.position = 'fixed';
	popPanelHTML.style.top = 0;
	popPanelHTML.style.right = 0;
	popPanelHTML.style.bottom = 0;
	popPanelHTML.style.left = 0;
	popPanelHTML.style.zIndex = 5001;
	popPanelHTML.innerHTML = '<div class="cm-panel">' +
        '<div margin-bottom: 20px; margin-top: 20px;>' +
        '<i class="fa fa-exclamation-circle fa-pull-left"></i>' +
    	'<h2>WMECM Update Notes</h2>' +
        '<hr class="cm-panel-hr"></div>' +
        '<div class="cm-panel-inner"><div class="cm-panel-section">' +
        updateText +
        '</div>' +
        '<div style="margin-top: 10px; font-size: 10.1pt">' +
        'For more details on the new features or to report a bug, please visit the forum post: <a href="' + forumURL + '" target="_blank"><i class="fa fa-external-link"></i></a>' +
        '</div>' +
        '<div style="margin-top: 10px; font-style: italic; font-size: 10px;"> ' +
        'WME Context Menu update for min. version ' + updateVersion +
        '</div></div>' +
        '<hr class="cm-panel-hr">' +
        '<div class="cm-btn-container">' +
        '<button id="btnCMokay" class="btn btn-default">OK</button>' +
        '</div></div>';

    document.body.appendChild(popPanelHTML);

    document.getElementById('btnCMokay').onclick = function() {
        document.getElementById('divCMupdate').remove();
    	document.getElementById('cmUpdateNote').classList.remove('cm-unread');
    	document.getElementById('cssCMupdate').remove();
    	requestAnimationFrame(function(){CMenuVersion.updateVersionString(minVersion)});
    };
};

//=======================================================================================
var switchPanelTo = function(panelName) {
    switch (panelName) {
        case 'clipboard':
            document.getElementById('cmClipboard').value = true;
            document.getElementById('cmClipboard').classList.remove('toggle-off');
            document.getElementById('cmRSel').value = false;
            document.getElementById('cmRSel').classList.add('toggle-off');
            document.getElementById('cmSpeed').value = false;
            document.getElementById('cmSpeed').parentNode.style.opacity = 0.4;
            document.getElementById('cmContextMenu').style.width = '210px';
            break;
        case 'rsel':
            document.getElementById('cmClipboard').value = false;
            document.getElementById('cmClipboard').classList.add('toggle-off');
            document.getElementById('cmRSel').value = true;
            document.getElementById('cmRSel').classList.remove('toggle-off');
            document.getElementById('cmSpeed').value = false;
            document.getElementById('cmSpeed').parentNode.style.opacity = 0.4;
            document.getElementById('cmContextMenu').style.width = '210px';
            break;
        case 'speed':
            document.getElementById('cmClipboard').value = false;
            document.getElementById('cmClipboard').classList.add('toggle-off');
            document.getElementById('cmRSel').value = false;
            document.getElementById('cmRSel').classList.add('toggle-off');
            document.getElementById('cmSpeed').value = true;
            document.getElementById('cmSpeed').parentNode.style.opacity = 0.84;
            document.getElementById('cmContextMenu').style.width = '220px';
            speedhelperHeight = null;
            break;
    }
};

var showEmptyPanel = function(panelName) {
    switch (panelName) {
        case 'clipboard':
            document.getElementById('cmMenuHeaderTitle').innerHTML = 'Copy To Clipboard';
            document.getElementById('cmMenuNoContent').style.display = 'block';
            document.getElementById('cmMenuContent').style.display = 'none';
            break;
        case 'rsel':
            if (document.getElementById('cmRSelAutoAdd')) document.getElementById('cmRSelAutoAdd').style.display = 'none';
            document.getElementById('cmMenuHeaderTitle').innerHTML = 'Send To Road Selector';
            document.getElementById('cmMenuNoContent').style.display = 'block';
            document.getElementById('cmMenuContent').style.display = 'none';
            break;
        case 'speed':
            document.getElementById('cmMenuHeaderTitle').innerHTML = 'Edit Speed Limits';
            document.getElementById('cmMenuNoContent').style.display = 'block';
            document.getElementById('cmMenuContent').style.display = 'none';
            break;
    }
};

//=======================================================================================
var initContextMenu = function () {
	cmlog([1,1], 'initContextMenu()');
    var contextMenuActiveArea = document.createElement('div'),
        contextMenu = document.createElement('div'),
        contextMenuCSS = document.createElement('style'),
        menuCSS;

    try {//opacity: 0.5;
        contextMenuCSS.type = 'text/css';
        menuCSS = `
        #cmContextMenu { display: block; opacity: 1; }
        #cmContextMenu.cm-drag { cursor: move; opacity: 0.75 !important; transition: opacity .1s linear 0s; }
        .cm-top { border-top-right-radius: 3px; border-top-left-radius: 3px; }
        .cm-bottom { border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; }
        #cmFooter+#cmContainer .cm-menu-header>dl, #cmFooter+#cmContainer .cm-menu-header>dl>dt, #cmFooter+#cmContainer .cm-rsel>dl, #cmFooter+#cmContainer .cm-rsel>dl>dt, .cm-rsel+.cm-menu-header>dl, .cm-rsel+.cm-menu-header>dl>dt { border-radius: 0; }
        #cmFooter+#cmContainer #cmMenuContent > div:last-child { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; }
        .cm-footer-icns { font-size: 13px; margin: 0px 4px 0px 2px; cursor: pointer; display: inline-table; line-height: 1; }
        .cm-footer-icns:active, .cm-footer-icns:focus, .cm-footer-icns.toggle-off:active, .cm-footer-icns.toggle-off:focus { color: #64D8EA; }
        .cm-footer-icns.toggle-off, .cm-footer-text.toggle-off { color: #8CBBCC; }
        .cm-update-note { z-index: 3; cursor: pointer; color: #59899E; margin-top: -15px; position: relative; float: right; right: 5px; bottom: 2px; opacity: .8; line-height: 1; font-size: 14px; }
        .cm-update-note.cm-unread { color: crimson; }
        div.cm-menu-header { padding: 0px; border: 0; height: 20px; }
        .cm-menu-header dl { background-color: rgba(147, 196, 211, 0.92); padding: 0; border-top-right-radius: 4px; border-top-left-radius: 4px; }
        .cm-menu-header dt { padding: 4px 11px; text-transform: uppercase; line-height: 1.3; background-color: rgba(111, 167, 185, 0.7); color: #D8E9EF; font-size: 10px; border-top-right-radius: 3px; border-top-left-radius: 3px; height: 22px; margin-top: 1px; box-shadow: 0px -1px 0px #9ACCDC; }
        div.cm-menu-section { z-index: 2; border-bottom: 1px solid #416B7C; padding: 2px 0px 3px; background-color: rgba(147, 196, 211, 0.92); }
        .cm-menu-section dl { margin: 0; padding: 0; display: block; }
        .cm-menu-section dt { font-size: 9px; padding: 0px 6px 0px 20px; margin-top: 2px; text-transform: uppercase; line-height: 1.2; }
        .cm-menu-section dd { display: inherit; padding: 0px 14px 0px 20px; font-size: 11px; color: #234350; font-weight: 600; line-height: 1.3; word-break: break-all;}
        .cm-menu-section dd:hover, .cm-menu-section dd:active, .cm-menu-section dd:focus { cursor: default; background-color: #BEDCE5; color: #416B7C; }
        .cm-menu-section dd:active { color: #64D8EA; }`;
        menuCSS += `
        .cm-menu-section .cm-hide.fa-caret-down, .cm-menu-section .cm-hide.fa-caret-up { position: absolute; left: 8px; width: 90%; cursor: pointer; }
        .cm-menu-section .cm-hide.fa-caret-down { display: block; }
        .cm-menu-section .cm-hide.fa-caret-up { display: none; }
        .cm-menu-section.cm-hidden .cm-hide.fa-caret-down { display: none; }
        .cm-menu-section.cm-hidden .cm-hide.fa-caret-up { display: block; }
        .cm-hidden { height: 19px; }
        .cm-hidden dd { display: none; }
        .cm-hidden dt, .cm-hidden dl {float: left; margin-right: -12px; color: rgba(255, 255, 255, 0.7); }
        .cm-hidden dl:nth-of-type(2)>dt:before { content: "/ "; }
        dd.cm-paste { font-style: italic; text-align: right; max-width: 50%; float: right; padding: 0px 10px 0px 5px;}
        .cm-paste:before { content: ""; font-style: normal; font-weight: 400; color: black; font-size: 11px; margin-right: 2px; }`;
        menuCSS += `
        dd.cm-rsel-goselect { padding-right: 12px; padding-left: 12px; cursor: pointer; }
        .cm-menu-section .cm-rsel-goselect:hover { background-color: transparent; color: #d4e7ed; }
        .cm-rsel { display: none; border: 0; background-color: rgba(154, 204, 220, 0.9); border-top-right-radius: 3px; border-top-left-radius: 3px; margin-bottom: -1px; }
        .cm-rsel>dl { padding-top: 1px; border-top-right-radius: 4px; border-top-left-radius: 4px; }
        .cm-rsel>dl>dt { background-color: rgba(111, 167, 185, 0.7); height: 20px; margin-top: -2px; border-top-right-radius: 3px; border-top-left-radius: 3px; padding: 4px 10px; color: #d8e9ef; font-size: 10px; }
        .cm-rsel>dl>dt+dd { height: 24px; margin: 0px 0px 3px; padding: 5px 10px 5px 9px; background-color: rgba(228, 248, 255, 0.3); box-shadow: 0px 1px 0px rgba(0,0,0,0.1); }
        .cm-rsel-options, cm-rsel-options:focus:hover, cm-rsel-options+input:not(checked) { z-index: 2; background-color: rgba(89, 137, 158, 0.60); color: #D4E7ED; font-weight: bold !important; height: 14px !important; line-height: 1; padding: 3px 6px; font-size: 8px !important; border-radius: 15px; }
        .cm-rsel-options:hover { background-color: rgba(89, 137, 158, 0.7); color: white; }
        .cm-rsel-options.active:hover { background-color: #274B5A }
        .cm-rsel-options.active, .cm-rsel-options:active, .cm-rsel-options:active:focus { color: white; background-color: #3F6271; }\
        .cm-rsel-options:focus { color: white; }
        .cm-rsel-options.and { background-color: #EEE; color: #59899E; border-right: 1px solid #AAA;}
        .cm-rsel-options.cm-badge-right { border-left: 1px solid #59899E; }`;
        menuCSS += `
        div#cmSpeedLimit { z-index: 3; width: 100%; font-size: 11px; text-align: center; display: inline-block; padding: 10px; }
        div#cmSpeedLimit input[type="number"] { height: 28px; width: 44px; font-size: 12px; padding: 4px 5px; line-height: 1; margin: 0px 2px; }
        div#cmSpeedLimit input[type="checkbox"]+label { color: transparent; width: 10px; margin-left: 5px; vertical-align: middle; }
        div#cmSpeedLimit .controls-container { margin-left: 5px; }
        div#cmSpeedLimit div { border-radius: 3px; display: inline-block; }
		#signsholder_cm>div#btnCMclearSLs { float: left; display: inline-block; cursor: pointer; background-image: none; color: #134C65; font-size: 28px; }
		#signsholder_cm>div#btnCMclearSLs>div {display: none;}
		#signsholder_cm>div#btnCMclearSLs:before { vertical-align: middle; }
		.cm-speed-limit>.form-inline .fa-ban { cursor: pointer; color: #1F576E; padding-right: 8px; font-size: 1.5em; font-weight: bold; vertical-align: middle; margin-bottom: 2px; }
		#btnCMclearSLs:hover, .cm-speed-limit>.form-inline .fa-ban:hover { color: #00ECE3; }
        div#signsholder_cm div:not(#btnCMclearSLs):before { opacity: .9; position: absolute; background-color: white; color: black; border: 3px solid #00ECE3; font-weight: 400; border-radius: 50%; font-size: 10px; width: 20px; height: 20px; line-height: 14px; text-align: center; margin-left: -10px; margin-top: -3px; box-shadow: 0px 1px 1px gray; }
        div#signsholder_cm .cm-a:before { content: "A"; }
        div#signsholder_cm .cm-b:before { content: "B"; }
        .cm-sl-verified { box-shadow: inset 0px 0px 0px 2px lime; }
        .cm-sl-unverified { box-shadow: inset 0px 0px 0px 2px gold; }
        .cm-sl-multisegs { box-shadow: inset 0px 0px 0px 3px #333 !important; }
        .cm-sl-verified.cm-both { box-shadow: 0px 0px 0px 1px lime, inset 0px 0px 0px 2px greenyellow; }
        .cm-sl-unverified.cm-one { box-shadow: 0px 0px 0px 1px lime, inset 0px 0px 0px 2px rgba(255, 235, 59, 1);  }
        .cm-sl-unverified.cm-both { box-shadow: 0px 0px 0px 1px orange, inset 0px 0px 0px 2px rgba(255, 235, 59, 1); }
        #signsholder_cm>#signsError { float: initial; width: 100%; height: initial; padding: 5px 5px 5px 45px !important; text-align: left; }`;
        contextMenuCSS.innerHTML = menuCSS;
        document.head.appendChild(contextMenuCSS);

        // reminder to self: no need to declare CSS here since this is put into DOM once and does not get destroyed
        contextMenu.id = 'cmContextMenu';
        contextMenu.style.position = 'fixed';
        contextMenu.style.top = '0px';
        contextMenu.style.left = '0px';
        contextMenu.style.width = '210px';
        contextMenu.style.margin = '0px';
        contextMenu.style.padding = '0px';
        contextMenu.style.borderRadius = '4px';
        contextMenu.style.boxShadow = '0px 5px 12px rgba(0, 0, 0, 0.5)';
        contextMenu.style.border = '1px solid rgb(89, 137, 150)';
        contextMenu.style.borderBottom = '1px solid #2D505F';
        contextMenu.style.color = 'white';
        contextMenu.style.zIndex = '5000';
        contextMenu.style.display = 'none';
        contextMenu.style.opacity = 0;
        contextMenu.innerHTML = '<div id="cmContainer" style="z-index: 2; color: white; padding:0; margin:0; position: relative; width: 100%; display: block;"></div>' +
        	'<div id="cmUpdateNote" class="fa fa-exclamation-circle cm-update-note"></div>' +
            '<div id="cmFooter" class="cm-bottom" style="color: #DDEDF3; height: 24px; background-color: rgba(75, 125, 148, 0.85); ' +
            'z-index: 3; padding: 1px 7px 1px; margin: 0; position: relative; width: 100%; display: block;"></div>';


        document.getElementById('map').appendChild(contextMenu);

        document.getElementById('cmFooter').innerHTML = `
        <div style="position: relative; float: left; vertical-align: middle;">
        <i id="cmPinMenu" class="fa fa-thumb-tack cm-footer-icns toggle-off" style="margin: 0px -3px 0px -3px; padding: 3px 6px;" value=true title="Pin menu" data-toggle="tooltips"></i>
        <span id="cmPinClose" class="cm-footer-text toggle-off" style="cursor: pointer; font-weight: 600; border-left: 1px solid #D4E7ED; padding-left: 8px; text-transform: uppercase; font-size: 10px; letter-spacing: .7px;">Close</span>
        </div>
        <div id="cmFooterCaret" class="fa fa-angle-up cm-footer-icns pull-right" style="position: relative; top: 3px; margin: auto 0px auto 7px; font-weight: bold;"></div>
        <div style="position: relative; height: 21px; border-radius: 20px; border: 1px solid #6EA1B7;  padding: 0px 1px 0px 5px;" class="btn-group pull-right">
          <div style="position: relative; display: inline-block; height: 100%; border-right: 1px solid #6EA1B7; padding-left: 2px; width: 22px;" class="">
            <i id="cmClipboard" value="false" style="font-size: 13px;" class="fa fa-clipboard cm-footer-icns toggle-off" title="Copy to Clipboard" data-toggle="tooltips"></i>
          </div>
          <div style="position: relative; display: inline-block; height: 100%; border-right: 1px solid #6EA1B7;">
            <i id="cmRSel" style="font-size: 14px;" value="false" class="fa fa-road cm-footer-icns toggle-off" title="Copy to WME Road Selector" data-toggle="tooltips"></i>
          </div>
          <div style="position: relative; display: inline-block; width: 20px;  margin: 0px 0px 0px -2px; opacity: 0.84;">
            <span id="cmSpeed" style="height: 16px" value="false" title="Edit Speed Limits" class="fa fa-stack">
              <i class="fa fa-circle cm-footer-icns" style="font-size: 15px; width: 15px; color: #EEE; line-height: 14px; position: absolute; left: 0; text-align: center;"></i>
              <i class="fa fa-circle-o cm-footer-icns" style="font-size: 15px; width: 15px; font-weight: 500; color: crimson; line-height: 14px; position: absolute; left: 0; text-align: center;"></i>
              <i class="fa cm-footer-icns" style="font-size: 8px; font-style: normal; width: 15px; color: black; line-height: 14px; position: absolute; left: 0; text-align: center;">S</i>
            </span>
          </div>
        </div>`;

        resetContextMenu(contextMenuSettings);
        hidePasteMenu();

        setTimeout(function () {
            if (contextMenuSettings.clipboard === 0 || (contextMenuSettings.clipboard === 1 && document.getElementById('tabRSel') === null))
                switchPanelTo('clipboard');
        }, 2000);

        if (contextMenuSettings.clipboard === 1) switchPanelTo('rsel');
        else if (contextMenuSettings.clipboard === 2) switchPanelTo('speed');

        if (contextMenuSettings.pin) {
            document.getElementById('cmPinMenu').value = true;
            document.getElementById('cmPinMenu').classList.remove('toggle-off');
            document.getElementById('cmPinClose').classList.remove('toggle-off');
        } else {
            document.getElementById('cmPinMenu').value = false;
            document.getElementById('cmPinMenu').classList.add('toggle-off');
            document.getElementById('cmPinClose').classList.add('toggle-off');
        }

        // VERSION CHECK
		if (!CMenuVersion.isUpToDate(minVersion)) {
			document.getElementById('cmUpdateNote').classList.add('cm-unread');
		}

		var forumURL = 'https://www.waze.com/forum/viewtopic.php?f=819&t=178371';
			updateNotes_0_2_2 = 'Hello there! Thanks for trying out WME Context Menu. Given the big push by Waze to add speed limits (SLs) to the map, this most recent update provides a few enhancements that I hope you\'ll find helpful in getting the job done:' +
			'</div><div class="cm-panel-section"><ul>' +
			'<li>Right-clicking directly on an unselected segment will now open the menu.</li>' +
			'<li>The menu can be dragged anywhere on the map when in pinned mode.</li>' +
			'<li>Only when pinned, numeric hotkeys (1-3) for switching panels of the context menu have been restricted to work only when your cursor is within the menu area. This is to prevent undesired capturing of keypresses.' +
			'<li>Verified Speedhelper signs are now highlighted with a green border. Unverified SLs are highlighted yellow.</li>' +
			'<li>Two-way roads with differing SL values for each direction are indicated by a start-node A or B badge on top of its sign.</li>' +
			'<li>You can now selectively assign SLs to either A-B or B-A direction: Use <b>shift+click for A-B</b> or <b>Alt+click for B-A</b>.</li>' +
			'<li>Speedhelper signs will automatically adjust the units to those used by the country</li>' +
			'</ul>' +
			'As a reminder, these enhanced features currenly apply to only the Speedhelper panel within WME Context Menu.';

	    document.getElementById('cmUpdateNote').onclick = function(e){
	    	e.stopPropagation();
	    	showPopupPanel(minVersion, updateNotes_0_2_2, forumURL);
	    };


        document.getElementById('cmClipboard').onclick = function (e) {
            try {
                e.stopPropagation();
                switchPanelTo('clipboard');
                contextMenuSettings.clipboard = 0;
                localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);

                var selectedStuff = selectedItemsIsSegment();
                if (selectedStuff) {
                    var segInfo = getSegmentProperties(selectedStuff);
                    populateCopyMenu(segInfo, contextMenuSettings);
                } else {
                    showEmptyPanel('clipboard');
                }
                hidePasteMenu(false);
            } catch (err) {
                console.error(err);
            };
        };

        document.getElementById('cmRSel').onclick = function (e) {
            try {
                e.stopPropagation();
                if (document.getElementById('tabRSel') !== null) {
                    switchPanelTo('rsel');
                    contextMenuSettings.clipboard = 1;
                    localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);

                    var selectedStuff = selectedItemsIsSegment();
                    if (selectedStuff) {
                        var segInfo = getSegmentProperties(selectedStuff);
                        if (document.getElementById('cmRSelAutoAdd')) document.getElementById('cmRSelAutoAdd').style.display = 'block';
                        populateCopyMenu(segInfo, contextMenuSettings);
                    } else {
                        showEmptyPanel('rsel');
                    }
                    hidePasteMenu(true);
                }
            } catch (err) {
                console.error(err);
            }
        };

        document.getElementById('cmSpeed').onclick = function (e) {
            try {
                e.stopPropagation();
                switchPanelTo('speed');
                contextMenuSettings.clipboard = 2;
                localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);

                var selectedStuff = selectedItemsIsSegment();
                if (selectedStuff) {
                    menuResetEvent_SL = true;
                    populateSpeedMenu(contextMenuSettings);
                    window.removeEventListener('click', closeContextMenu, false);
                    document.getElementById('toolbar').removeEventListener('mouseenter', closeContextMenu, false);
                } else {
                    showEmptyPanel('speed');
                }
            } catch (err) {
                console.error(err);
            }
        };

        document.getElementById('map').addEventListener(
            'contextmenu',
            function (e) {
				cmlog([1,0],'------------------------------------------------------------')
            	cmlog([1,1], 'contextmenu');
            	//console.info(e);
                var selectedStuff = selectionIsSegment(e),
                	contextMenu = document.getElementById('cmContextMenu');

                if (selectedStuff) {
                    try {
                        e.stopPropagation();
                        contextMenu.style.display = 'block';
                        contextMenu.style.top = e.clientY - 10 + 'px';
                        contextMenu.style.left = e.clientX + 'px';
                        contextMenu.classList.remove('cm-drag');

                        menuResetEvent_SL = true;
                        setupSegmentContextMenu(e);
                        contextMenu.style.opacity = 1;

                        window.addEventListener('keydown', menuShortcutKeys, true);
                        //console.info('WMECM:','Added initial global hotkey listener upon menu open');
                        if (document.getElementById('cmPinMenu').value) {
		                    // use a more selective hotkey listener
		                    //console.info('WMECM:','Menu is pinned, so adding selective hotkey listeners too');
							contextMenu.addEventListener('mouseenter', addHotkeyListener, false);
							contextMenu.addEventListener('mouseleave', removeHotkeyListener, false);

					        Waze.selectionManager.events.register("selectionchanged", null, setupSegmentContextMenu);
	                    }
                    } catch (err) { console.error(err); }
                } else {
                	// No segment detected... Decide whether to keep the menu open.
                    if (document.getElementById('cmPinMenu').value) {
                        return true;
                    } else {
                        contextMenu.style.display = 'none';
                        return false;
                    }
                }
            }, true);

        document.getElementById('cmPinMenu').onclick = function (e) {
            try {
                e.stopPropagation();
                if (this.value) { // no pinning
                    this.value = false;
                    this.classList.add('toggle-off');
                    document.getElementById('cmPinClose').classList.add('toggle-off');

                    // remove drag menu listeners
                    dragMenuSetup(false);

                    // closing context menu will remove hotkey listeners & closing contextmenu listeners
                    // they will be reinstated if appropriate upon reopening the menu
                    closeContextMenu();
                } else { // pin menu
                    this.value = true;
                    this.classList.remove('toggle-off');
                    document.getElementById('cmPinClose').classList.remove('toggle-off');

                    // remove listeners that close the menu without clicking close
                    window.removeEventListener('click', closeContextMenu, false);
                    document.getElementById('toolbar').removeEventListener('mouseenter', closeContextMenu, false);

                    // Add dragging menu listeners
                    dragMenuSetup(true);

                    // use a more selective hotkey listener
                    //console.info('WMECM:','Switch to menu pinning, so adding selective hotkey listener');
					document.getElementById('cmContextMenu').addEventListener('mouseenter', addHotkeyListener, false);
					document.getElementById('cmContextMenu').addEventListener('mouseleave', removeHotkeyListener, false);
                }
                contextMenuSettings.pin = !contextMenuSettings.pin;
                localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);
            } catch (err) {
                console.error(err);
            }
        };

        document.getElementById('cmPinClose').onclick = closeContextMenu;

        document.getElementById('cmFooterCaret').addEventListener('click', function (e) {
            e.stopPropagation();
            if (this.classList.contains('fa-angle-up')) { //switch to top menubar
                contextMenuSettings.position = 1;
                localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);
                adjustContextMenubar(1);
            } else if (this.classList.contains('fa-angle-down')) { //switch to bottom menubar
                contextMenuSettings.position = 0;
                localStorage.WME_ContextMenu = JSON.stringify(contextMenuSettings);
                adjustContextMenubar(0);
            }
        }, false);

    } catch (err) {
        console.error(err);
    }

    dragMenuSetup();

    var cmWaitCover = document.createElement('div');
    cmWaitCover.id = 'cmWaitCover';
	cmWaitCover.style.display = 'none';
	cmWaitCover.style.position = 'fixed';
	cmWaitCover.style.top = 0; cmWaitCover.style.bottom = '25px'; cmWaitCover.style.left = 0; cmWaitCover.style.right = 0;
	cmWaitCover.style.background = 'transparent';
	cmWaitCover.style.cursor = 'wait';
	cmWaitCover.style.zIndex = '5001';
	document.body.appendChild(cmWaitCover);

    setTimeout(function () {
        try {
            var rselBtnEls = document.querySelectorAll('#RSconditions button');
            for (var b = rselBtnEls.length; b--;) {
                rselBtnEls[b].addEventListener('click', function () {
                    this.classList.remove('btn-info')
                }, false);
            }
        } catch (err) {
            console.error(err)
        }
    }, 1200)

};

var waitCount = 0,
    maxWaitCount = 50;
var waitForWaze = function () {
    try {
        if (document.getElementById('cmContextMenu')) {
            return true;
        } else if (typeof(Waze) !== "undefined" && Waze.model && Waze.selectionManager &&
        	Waze.selectionManager.selectedItems &&
            Waze.model.segments && Waze.model.cities &&
            Waze.map && Waze.map.layers) {

            //cmlog([1],'starting...');
            setTimeout(initContextMenu, 1000);
            //console.info('WMECM:', 'Something may have went wrong... not sure.');
        } else if (waitCount++ < maxWaitCount) {
            //console.info('waiting...');
            setTimeout(waitForWaze, 1000);
        } else {
            console.error('WMECM:', 'Failed to start');
        }
    } catch (err) {
        console.error('WMECM:', 'Whoa. Major fail. Please let TheLastTaterTot know about this...');
        console.error(err);
    }
};

waitForWaze();