WME Road Selector

Makes selection of multiple roads based on given condition

目前為 2016-06-29 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name				WME Road Selector
// @description 		Makes selection of multiple roads based on given condition
// @include 			https://www.waze.com/editor/*
// @include 			https://www.waze.com/*/editor/*
// @include 			https://editor-beta.waze.com/*
// @version 			1.21
// @copyright			2016, pvo11
// @namespace			https://greasyfork.org/scripts/3462-wme-road-selector
// ==/UserScript==

var StringOps = {
	0: "=",
	1: "!=",
	2: "contains",
	3: "! contains",
	4: "~"
};

function populateStringOps(sel)
{
	var selectStringOp = getId(sel);

	for (var id in StringOps) {
		var txt = StringOps[id];
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		if (id === 0) {
			usrOption.setAttribute('selected',true);
		}
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectStringOp.appendChild(usrOption);
	}
}


var IntegerOps = {
	0: "=",
	1: "!=",
	2: ">",
	3: ">=",
	4: "<",
	5: "<="
};

function populateIntegerOps(sel)
{
	var selectIntegerOp = getId(sel);

	for (var id in IntegerOps) {
		var txt = IntegerOps[id];
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		if (id === 0) {
			usrOption.setAttribute('selected',true);
		}
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectIntegerOp.appendChild(usrOption);
	}
}


var EqualOps = {
	0: "=",
	1: "!="
};

function populateEqualOps(sel)
{
	var selectEqualOp = getId(sel);

	for (var id in EqualOps) {
		var txt = EqualOps[id];
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		if (id === 0) {
			usrOption.setAttribute('selected',true);
		}
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectEqualOp.appendChild(usrOption);
	}
}


var DirOps = {
	0: "none",
	1: "any",
	2: "both",
	3: "A->B",
	4: "B->A",
	5: "hidden",
	6: "unverified",
	7: "only one"
};

function populateDirOps(sel, unver)
{
	var selectDirOp = getId(sel);
	for (var id in DirOps) {
		if (unver || id !== '6') {
			var txt = DirOps[id];
			var usrOption = document.createElement('option');
			var usrText = document.createTextNode(txt);
			if (id === 0) {
				usrOption.setAttribute('selected',true);
			}
			usrOption.setAttribute('value',id);
			usrOption.appendChild(usrText);
			selectDirOp.appendChild(usrOption);
		}
	}
}


var Countries = {};

function populateCountries(sel)
{
	var selectCountry = getId(sel);
	Countries = {};

	for (var countryID in  Waze.model.countries.objects) {
		Countries[countryID] = Waze.model.countries.get(countryID).name;
	}


	for (var id in Countries) {
		var txt = Countries[id];
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectCountry.appendChild(usrOption);
	}
}


var RoadTypes = {
	101: "--- Highways ---",
	3: "Freeway",
	6: "Major Highway",
	7: "Minor Highway",
	4: "Ramp",
	102: "--- Streets ---",
	2: "Primary Street",
	1: "Street",
	103: "--- Other drivable ---",
	8: "Dirt road / 4x4",
	20: "Parking Lot",
	17: "Private Road",
	104: "--- Non-drivable ---",
	5: "Walking Trail",
	10: "Pedestrian Bw.",
	16: "Stairway",
	18: "Railroad",
	19: "Runway/Taxiway",
	15: "Ferry"
};
var RoadTypesOrder = [101, 3, 6, 7, 4, 102, 2, 1, 103, 8, 20, 17, 104, 5, 10, 16, 18, 19, 15];

function populateRoadTypes(sel)
{
	var selectRoadType = getId(sel);

	for (i = 0; i < RoadTypesOrder.length; i++) {
		var id = RoadTypesOrder[i];
		var txt = RoadTypes[id];
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		if (id == 1) {
			usrOption.setAttribute('selected',true);
		}
		if (id > 100) {
			usrOption.setAttribute('disabled',true);
			usrOption.setAttribute('style','font-weight: bold');
		}
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectRoadType.appendChild(usrOption);
	}
}


var Directions = {
	0: "Two way (=)",
	1: "One way (A->B)",
	2: "One way (B->A)",
	3: "Unknown"
};

function populateDirections(sel)
{
	var selectDirection = getId(sel);

	for (var id in Directions) {
		var txt = Directions[id];
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		if (id === 0) {
			usrOption.setAttribute('selected',true);
		}
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectDirection.appendChild(usrOption);
	}
}


function populateElevations(sel)
{
	var selectElevation = getId(sel);

	for (var id = 9; id >= -5; id--) {
		var txt;
		if (id === 0) {
			txt = "Ground";
		} else {
			txt = String(id);
		}
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		if (id === 0) {
			usrOption.setAttribute('selected',true);
		}
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectElevation.appendChild(usrOption);
	}
}


function populateLocks(sel)
{
	var selectLock = getId(sel);

	for (var id = 1; id <= 6; id++) { 
		var txt = String(id);
		var usrOption = document.createElement('option');
		var usrText = document.createTextNode(txt);
		if (id === 1) {
			usrOption.setAttribute('selected',true);
		}
		usrOption.setAttribute('value',id);
		usrOption.appendChild(usrText);
		selectLock.appendChild(usrOption);
	}
}


var milesConst = 1.609344;

function km2miles(km) {
  return Math.round(km / milesConst);
}


function miles2km(m) {
  return Math.round(m * milesConst);
}


function decimalRound(value, exp) {
	if (typeof exp === 'undefined' || +exp === 0) {
		return Math.round(value);
	}
	value = +value;
	exp = +exp;
	if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
		return NaN;
	}
	value = value.toString().split('e');
	value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
	value = value.toString().split('e');
	return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
}


var ExprStatus = 0;
var ExprTree = {};
var BktTrees = {};
var BktOps = {};
var BktCount = 0;
var hasStates;
var speedInMiles;


function checkExpr(tree, segment)
{
		if (typeof (tree.type) === 'undefined') {
		return false;
	}

	var result;
	switch (tree.type) {
		case "Country":
			var sid = segment.attributes.primaryStreetID;
			if (typeof(sid) === 'undefined' ||  sid === null) {
				result = false;
			} else {
				var street = Waze.model.streets.get(sid);
				if (typeof(street) === 'undefined') {
					result = false;
				} else {
					var countryID = Waze.model.cities.get(street.cityID).countryID;
	
					if (tree.op === "0") {
						result = tree.id == countryID;
					} else {
						result = tree.id != countryID;
					}
				}
			}
			break;
		case "State":
			var sid = segment.attributes.primaryStreetID;
			if (typeof(sid) === 'undefined' ||  sid === null) {
				result = false;
			} else {
				var street = Waze.model.streets.get(sid);
				if (typeof(street) === 'undefined') {
					result = false;
				} else {
					var stateID = Waze.model.cities.get(street.cityID).stateID;
					if (typeof(street) === 'undefined') {
						result = false;
					} else {
						var stateName = Waze.model.states.get(stateID).name;
						if (stateName === null) {
							stateName = "";
						}
						var condTxt = tree.txt;
						if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
							condTxt = condTxt.toLowerCase();
							stateName = stateName.toLowerCase();
						}
						switch (tree.op) {
							case "0":
								result = stateName == condTxt;
								break;
							case "1":
								result = stateName != condTxt;
								break;
							case "2":
								result = stateName.indexOf(condTxt) >= 0;
								break;
							case "3":
								result = stateName.indexOf(condTxt) < 0;
								break;
							default:
								var re;
								if (getId("cbRSCaseSens").checked) {
									re = new RegExp(condTxt);
							    } else {
									re = new RegExp(condTxt, "i");
							    }
								result = stateName.search(re) >= 0;
								break;
						}
					}
				}
			}
			break;
		case "City":
		case "XCity":
			var sid = segment.attributes.primaryStreetID;
			if (typeof(sid) === 'undefined' ||  sid === null) {
				result = false;
			} else {
				var street = Waze.model.streets.get(sid);
				if (typeof(street) === 'undefined') {
					result = false;
				} else {
					var cityName = Waze.model.cities.get(street.cityID).name;
					if (cityName === null) {
						cityName = "";
					}
					var condTxt = tree.txt;
					if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
						condTxt = condTxt.toLowerCase();
						cityName = cityName.toLowerCase();
					}
					switch (tree.op) {
						case "0":
							result = cityName == condTxt;
							break;
						case "1":
							result = cityName != condTxt;
							break;
						case "2":
							result = cityName.indexOf(condTxt) >= 0;
							break;
						case "3":
							result = cityName.indexOf(condTxt) < 0;
							break;
						default:
							var re;
							if (getId("cbRSCaseSens").checked) {
								re = new RegExp(condTxt);
						    } else {
								re = new RegExp(condTxt, "i");
						    }
							result = cityName.search(re) >= 0;
							break;
					}
				}
			}
			if  (tree.type === "City") {
				break;
			}
		case "ACity":
			if  (tree.type === "ACity") {
				result = false;
			}
			for(i = 0; i < segment.attributes.streetIDs.length; i++){
				var sid = segment.attributes.streetIDs[i];
				if (sid !== null) {
					var street = Waze.model.streets.get(sid);
					if (typeof(street) === 'undefined') {
						result = false;
					} else {
						var cityName = Waze.model.cities.get(street.cityID).name;
						if (cityName === null) {
							cityName = "";
						}
						var condTxt = tree.txt;
						if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
							condTxt = condTxt.toLowerCase();
							cityName = cityName.toLowerCase();
						}
						switch (tree.op) {
							case "0":
								result = result || (cityName == condTxt);
								break;
							case "1":
								result = result || (cityName != condTxt);
								break;
							case "2":
								result = result || (cityName.indexOf(condTxt) >= 0);
								break;
							case "3":
								result = result || (cityName.indexOf(condTxt) < 0);
								break;
							default:
								var re;
								if (getId("cbRSCaseSens").checked) {
									re = new RegExp(condTxt);
							    } else {
									re = new RegExp(condTxt, "i");
							    }
								result = result || (cityName.search(re) >= 0);
								break;
						}
					}
				}
			}
			break;
		case "Street":
		case "XStreet":
			var sid = segment.attributes.primaryStreetID;
			if (typeof(sid) === 'undefined' ||  sid === null) {
				result = false;
			} else {
				var street = Waze.model.streets.get(sid);
				if (typeof(street) === 'undefined') {
					result = false;
				} else {
					var streetName = street.name;
					if (streetName === null) {
						streetName = "";
					}
					var condTxt = tree.txt;
					if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
						condTxt = condTxt.toLowerCase();
						streetName = streetName.toLowerCase();
					}
					switch (tree.op) {
						case "0":
							result = streetName == condTxt;
							break;
						case "1":
							result = streetName != condTxt;
							break;
						case "2":
							result = streetName.indexOf(condTxt) >= 0;
							break;
						case "3":
							result = streetName.indexOf(condTxt) < 0;
							break;
						default:
							var re;
							if (getId("cbRSCaseSens").checked) {
								re = new RegExp(condTxt);
						    } else {
								re = new RegExp(condTxt, "i");
						    }
							result = streetName.search(re) >= 0;
							break;
					}
				}
			}
			if  (tree.type === "Street") {
				break;
			}
		case "AStreet":
			if  (tree.type === "AStreet") {
				result = false;
			}
			for(i = 0; i < segment.attributes.streetIDs.length; i++){
				var sid = segment.attributes.streetIDs[i];
				if (sid !== null) {
					var street = Waze.model.streets.get(sid);
					var street = Waze.model.streets.get(sid);
					if (typeof(street) === 'undefined') {
						result = false;
					} else {
						var streetName = street.name;
						if (streetName === null) {
							streetName = "";
						}
						var condTxt = tree.txt;
						if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
							condTxt = condTxt.toLowerCase();
							streetName = streetName.toLowerCase();
						}
						switch (tree.op) {
							case "0":
								result = result || (streetName == condTxt);
								break;
							case "1":
								result = result || (streetName != condTxt);
								break;
							case "2":
								result = result || (streetName.indexOf(condTxt) >= 0);
								break;
							case "3":
								result = result || (streetName.indexOf(condTxt) < 0);
								break;
							default:
								var re;
								if (getId("cbRSCaseSens").checked) {
									re = new RegExp(condTxt);
							    } else {
									re = new RegExp(condTxt, "i");
							    }
								result = result || (streetName.search(re) >= 0);
								break;
						}
					}
				}
			}
			break;
		case "NoName":
			if (tree.op) {
				result = typeof(segment.attributes.primaryStreetID) === 'undefined' || segment.attributes.primaryStreetID === null;
			} else {
				result = typeof(segment.attributes.primaryStreetID) !== 'undefined' && segment.attributes.primaryStreetID !== null;
			}
			break;
		case "ANoName":
			if (tree.op) {
				result = segment.attributes.streetIDs.length === 0;
			} else {
				result = segment.attributes.streetIDs.length > 0;
			}
			break;
		case "RoadType":
			if (tree.op === "0") {
				result = tree.id == segment.attributes.roadType;
			} else {
				result = tree.id != segment.attributes.roadType;
			}
			break;
		case "IsRound":
			if (tree.op) {
				result = segment.attributes.junctionID !== null;
			} else {
				result = segment.attributes.junctionID === null;
			}
			break;
		case "IsToll":
			if (tree.op) {
				result = segment.isTollRoad();
			} else {
				result = !segment.isTollRoad();
			}
			break;
		case "Direction":
			var dir = 0;
			if (!segment.attributes.fwdDirection) {
				dir += 2;
			}
			if (!segment.attributes.revDirection) {
				dir += 1;
			}
			if (tree.op === "0") {
				result = tree.id == dir;
			} else {
				result = tree.id != dir;
			}
			break;
		case "Elevation":
			switch (tree.op) {
				case "0":
					result = parseInt(tree.id, 10) === segment.attributes.level;
					break;
				case "1":
					result = parseInt(tree.id, 10) !== segment.attributes.level;
					break;
				case "2":
					result = parseInt(tree.id, 10) < segment.attributes.level;
					break;
				case "3":
					result = parseInt(tree.id, 10) <= segment.attributes.level;
					break;
				case "4":
					result = parseInt(tree.id, 10) > segment.attributes.level;
					break;
				default:
					result = parseInt(tree.id, 10) >= segment.attributes.level;
					break;
			}
			break;
		case "Tunnel":
			if (tree.op) {
				result = segment.isTunnel();
			} else {
				result = !segment.isTunnel();
			}
			break;
		case "ManLock":
			if	(segment.attributes.lockRank === null) {
				result = false;
			} else {
				switch (tree.op) {
					case "0":
						result = parseInt(tree.id, 10) === segment.attributes.lockRank + 1;
						break;
					case "1":
						result = parseInt(tree.id, 10) !== segment.attributes.lockRank + 1;
						break;
					case "2":
						result = parseInt(tree.id, 10) < segment.attributes.lockRank + 1;
						break;
					case "3":
						result = parseInt(tree.id, 10) <= segment.attributes.lockRank + 1;
						break;
					case "4":
						result = parseInt(tree.id, 10) > segment.attributes.lockRank + 1;
						break;
					default:
						result = parseInt(tree.id, 10) >= segment.attributes.lockRank + 1;
						break;
				}
			}
			break;
		case "TrLock":
			if	(segment.attributes.lockRank === null) {
				switch (tree.op) {
					case "0":
						result = parseInt(tree.id, 10) === segment.attributes.rank + 1;
						break;
					case "1":
						result = parseInt(tree.id, 10) !== segment.attributes.rank + 1;
						break;
					case "2":
						result = parseInt(tree.id, 10) < segment.attributes.rank + 1;
						break;
					case "3":
						result = parseInt(tree.id, 10) <= segment.attributes.rank + 1;
						break;
					case "4":
						result = parseInt(tree.id, 10) > segment.attributes.rank + 1;
						break;
					default:
						result = parseInt(tree.id, 10) >= segment.attributes.rank + 1;
						break;
				}
			} else {
				result = false;
			}
			break;
		case "Speed":
			switch (tree.op) {
				case "0":
					if (tree.txt === '') {
						result =  (!segment.attributes.fwdDirection || typeof(segment.attributes.fwdMaxSpeed) === 'undefined' || parseInt(segment.attributes.fwdMaxSpeed, 10) === null) &&
							(!segment.attributes.revDirection || typeof(segment.attributes.revMaxSpeed) === 'undefined' || segment.attributes.revMaxSpeed === null);
					} else {
						if (speedInMiles) {
							result = (!segment.attributes.fwdDirection || (parseInt(segment.attributes.fwdMaxSpeed, 10) !== parseInt(tree.txt, 10) && parseInt(segment.attributes.fwdMaxSpeed, 10) !== parseInt(tree.txt, 10) - 1)) &&
								(!segment.attributes.revDirection || (parseInt(segment.attributes.revMaxSpeed, 10) !== parseInt(tree.txt, 10) && parseInt(segment.attributes.revMaxSpeed, 10) !== parseInt(tree.txt, 10) - 1));
						} else {
							result = (!segment.attributes.fwdDirection || parseInt(segment.attributes.fwdMaxSpeed, 10) !== parseInt(tree.txt, 10)) &&
								(!segment.attributes.revDirection || parseInt(segment.attributes.revMaxSpeed, 10) !== parseInt(tree.txt, 10));
						}
					}
					break;
				case "1":
					if (tree.txt === '') {
						result = (segment.attributes.fwdDirection && typeof(segment.attributes.fwdMaxSpeed) !== 'undefined' && segment.attributes.fwdMaxSpeed !== null) ||
							(segment.attributes.revDirection && typeof(segment.attributes.revMaxSpeed) !== 'undefined' && segment.attributes.revMaxSpeed !== null);
					} else {
						if (speedInMiles) {
							result = (segment.attributes.fwdDirection && (parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) - 1)) ||
								 (segment.attributes.revDirection && (parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) - 1));
						} else {
							result = (segment.attributes.fwdDirection && parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10)) ||
								 (segment.attributes.revDirection && parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10));
						}
					}
					break;
				case "2":
					if (tree.txt === '') {
						result = (segment.attributes.fwdDirection && typeof(segment.attributes.fwdMaxSpeed) !== 'undefined' && segment.attributes.fwdMaxSpeed !== null) &&
							(segment.attributes.revDirection && typeof(segment.attributes.revMaxSpeed) !== 'undefined' && segment.attributes.revMaxSpeed !== null);
					} else {
						if (speedInMiles) {
							result = (segment.attributes.fwdDirection && (parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) - 1)) &&
								 (segment.attributes.revDirection && (parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) - 1));
						} else {
							result = (segment.attributes.fwdDirection && parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10)) &&
								 (segment.attributes.revDirection && parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10));
						}
					}
					break;
				case "3":
					if (tree.txt === '') {
						result = segment.attributes.fwdDirection && typeof(segment.attributes.fwdMaxSpeed) !== 'undefined' && segment.attributes.fwdMaxSpeed !== null;
					} else {
						if (speedInMiles) {
							result = segment.attributes.fwdDirection && (parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) - 1);
						} else {
							result = segment.attributes.fwdDirection && parseInt(segment.attributes.fwdMaxSpeed, 10) === tree.txt;
						}
					}
					break;
				case "4":
					if (tree.txt === '') {
						result = segment.attributes.revDirection && typeof(segment.attributes.revMaxSpeed) !== 'undefined' && segment.attributes.revMaxSpeed !== null;
					} else {
						if (speedInMiles) {
							result = segment.attributes.revDirection && (parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) - 1);
						} else {
							result = segment.attributes.revDirection && parseInt(segment.attributes.revMaxSpeed, 10) === tree.txt;
						}
					}
					break;
				case "5":
					if (tree.txt === '') {
						result = (!segment.attributes.fwdDirection && typeof(segment.attributes.fwdMaxSpeed) !== 'undefined' && segment.attributes.fwdMaxSpeed !== null) ||
							(!segment.attributes.revDirection && typeof(segment.attributes.revMaxSpeed) !== 'undefined' && segment.attributes.revMaxSpeed !== null);
					} else {
						if (speedInMiles) {
							result = (!segment.attributes.fwdDirection && (parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) - 1)) ||
								 (!segment.attributes.revDirection && (parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) - 1));
						} else {
							result = (!segment.attributes.fwdDirection && parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10)) ||
								 (!segment.attributes.revDirection && parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10));
						}
					}
					break;
				case "6":
					if (tree.txt === '') {
						result = (segment.attributes.fwdDirection && segment.attributes.fwdMaxSpeedUnverified && typeof(segment.attributes.fwdMaxSpeed) !== 'undefined' && segment.attributes.fwdMaxSpeed !== null) ||
							(segment.attributes.revDirection && segment.attributes.revMaxSpeedUnverified && typeof(segment.attributes.revMaxSpeed) !== 'undefined' && segment.attributes.revMaxSpeed !== null);
					} else {
						if (speedInMiles) {
							result = (segment.attributes.fwdDirection && segment.attributes.fwdMaxSpeedUnverified && (parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) - 1)) ||
								 (segment.attributes.revDirection && segment.attributes.revMaxSpeedUnverified && (parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) - 1));
						} else {
							result = (segment.attributes.fwdDirection && segment.attributes.fwdMaxSpeedUnverified && parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10)) ||
								 (segment.attributes.revDirection && segment.attributes.revMaxSpeedUnverified && parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10));
						}
					}
					break;
				default:
					if (tree.txt === '') {
						result = (segment.attributes.fwdDirection && typeof(segment.attributes.fwdMaxSpeed) !== 'undefined' && segment.attributes.fwdMaxSpeed !== null) !=
							(segment.attributes.revDirection && typeof(segment.attributes.revMaxSpeed) !== 'undefined' && segment.attributes.revMaxSpeed !== null);
					} else {
						if (speedInMiles) {
							result = (segment.attributes.fwdDirection && (parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10) - 1)) !=
							 	(segment.attributes.revDirection && (parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) || parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10) - 1));
						} else {
							result = (segment.attributes.fwdDirection && parseInt(segment.attributes.fwdMaxSpeed, 10) === parseInt(tree.txt, 10)) !=
							 	(segment.attributes.revDirection && parseInt(segment.attributes.revMaxSpeed, 10) === parseInt(tree.txt, 10));
						}
					}
					break;
			}
			break;
		case "SpdC":
			switch (tree.op) {
				case "0":
					result =  (!segment.attributes.fwdDirection || (segment.attributes.fwdFlags & 1) !== 1) &&
						(!segment.attributes.revDirection || (segment.attributes.revFlags & 1) !== 1);
					break;
				case "1":
					result = (segment.attributes.fwdDirection && (segment.attributes.fwdFlags & 1) === 1) ||
						(segment.attributes.revDirection && (segment.attributes.revFlags & 1) === 1);
					break;
				case "2":
					result = (segment.attributes.fwdDirection && (segment.attributes.fwdFlags & 1) === 1) &&
						(segment.attributes.revDirection && (segment.attributes.revFlags & 1) === 1);
					break;
				case "3":
						result = segment.attributes.fwdDirection && (segment.attributes.fwdFlags & 1) === 1;
					break;
				case "4":
					result = segment.attributes.revDirection && (segment.attributes.revFlags & 1) === 1;
					break;
				case "5":
					result = (!segment.attributes.fwdDirection && (segment.attributes.fwdFlags & 1) === 1) ||
						(!segment.attributes.revDirection && (segment.attributes.revFlags & 1) === 1);
					break;
				default:
					result = (segment.attributes.fwdDirection && (segment.attributes.fwdFlags & 1) === 1) !=
						(segment.attributes.revDirection && (segment.attributes.revFlags & 1) === 1);
					break;
			}
			break;
		case "IsNew":
			if (tree.op) {
				result = segment.isNew();
			} else {
				result = !segment.isNew();
			}
			break;
		case "IsChngd":
			if (tree.op) {
				result = !segment.isUnchanged();
			} else {
				result = segment.isUnchanged();
			}
			break;
		case "OnScr":
			var e = Waze.map.getExtent();
			var eg = e.toGeometry();
			var os = eg.intersects(segment.geometry);
			if (tree.op) {
				result = os;
			} else {
				result = !os;
			}
			break;
		case "Restr":
			if (tree.op) {
				result = segment.getRestrictionCount() !== 0;
			} else {
				result = segment.getRestrictionCount() === 0;
			}
			break;
		case "Clsr":
			if (typeof(tree.op) === "boolean") {
				if (tree.op) {
					result = segment.hasClosures();
				} else {
					result = !segment.hasClosures();
				}
			} else {
				if (segment.hasClosures()) {
					result = false;
					for (var closure in Waze.model.roadClosures.objects) {
						if (Waze.model.roadClosures.objects[closure].segID === segment.attributes.id) {
							var cmpDateTxt;
							if (tree.op >= 6) {
								cmpDateTxt = Waze.model.roadClosures.objects[closure].endDate;
							} else {
								cmpDateTxt = Waze.model.roadClosures.objects[closure].startDate;
							}
							var cmpDateArr = cmpDateTxt.match(/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/);
							var cmpDate = new Date(parseInt(cmpDateArr[1], 10), parseInt(cmpDateArr[2], 10) - 1, parseInt(cmpDateArr[3], 10), parseInt(cmpDateArr[4], 10), parseInt(cmpDateArr[5], 10), 0, 0);
							var cmpDays = Math.floor((cmpDate.getTime() - new Date().getTime()) / 86400000);
							switch (tree.op) {
								case 0:
								case 6:
									result = parseInt(tree.txt, 10) > cmpDays;
									break;
								case 1:
								case 7:
									result = parseInt(tree.txt, 10) <= cmpDays;
									break;
								case 2:
								case 8:
									result = parseInt(tree.txt, 10) < cmpDays;
									break;
								case 3:
								case 9:
									result = parseInt(tree.txt, 10) >= cmpDays;
									break;
								case 4:
								case 10:
									result = parseInt(tree.txt, 10) === cmpDays;
									break;
								case 5:
								case 11:
									result = parseInt(tree.txt, 10) !== cmpDays;
									break;
								default:
									result = false;
									break;
							}
						}
					}
				} else {
					result = false;
				}
			}
			break;
		case "ClReas":
			if (segment.hasClosures()) {
				for (var closure in Waze.model.roadClosures.objects) {
					if (Waze.model.roadClosures.objects[closure].segID === segment.attributes.id) {
						var condTxt = tree.txt;
						var reason = Waze.model.roadClosures.objects[closure].reason;
						if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
							condTxt = condTxt.toLowerCase();
							reason = reason.toLowerCase();
						}
						switch (tree.op) {
							case "0":
								result = reason == condTxt;
								break;
							case "1":
								result = reason != condTxt;
								break;
							case "2":
								result = reason.indexOf(condTxt) >= 0;
								break;
							case "3":
								result = reason.indexOf(condTxt) < 0;
								break;
							default:
								var re;
								if (getId("cbRSCaseSens").checked) {
									re = new RegExp(condTxt);
							    } else {
									re = new RegExp(condTxt, "i");
							    }
								result = reason.search(re) >= 0;
								break;
						}
					}
				}
			} else {
				result = false;
			}
			break;
		case "Updtd":
			var name;
			if (typeof(segment.attributes.updatedBy) === 'undefined' ||  sid === null) {
				name = '';
			} else {
				name = Waze.model.users.get(segment.attributes.updatedBy).userName;
				if ((typeof(name) === "undefined") || (name === null)) {
					return false;
				}
			}
			var condTxt = tree.txt;
			if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
				condTxt = condTxt.toLowerCase();
				name = name.toLowerCase();
			}
			switch (tree.op) {
				case "0":
					result = name == condTxt;
					break;
				case "1":
					result = name != condTxt;
					break;
				case "2":
					result = name.indexOf(condTxt) >= 0;
					break;
				case "3":
					result = name.indexOf(condTxt) < 0;
					break;
				default:
					var re;
					if (getId("cbRSCaseSens").checked) {
						re = new RegExp(condTxt);
				    } else {
						re = new RegExp(condTxt, "i");
				    }
					result = name.search(re) >= 0;
					break;
			}
			break;
		case "Crtd":
			var name;
			if (typeof(segment.attributes.createdBy) === 'undefined' ||  sid === null) {
				name = '';
			} else {
				name = Waze.model.users.get(segment.attributes.createdBy).userName;
				if ((typeof(name) === "undefined") || (name === null)) {
					return false;
				}
			}
			var condTxt = tree.txt;
			if ((!getId("cbRSCaseSens").checked) && (tree.op != "4")) {
				condTxt = condTxt.toLowerCase();
				name = name.toLowerCase();
			}
			switch (tree.op) {
				case "0":
					result = name == condTxt;
					break;
				case "1":
					result = name != condTxt;
					break;
				case "2":
					result = name.indexOf(condTxt) >= 0;
					break;
				case "3":
					result = name.indexOf(condTxt) < 0;
					break;
				default:
					var re;
					if (getId("cbRSCaseSens").checked) {
						re = new RegExp(condTxt);
				    } else {
						re = new RegExp(condTxt, "i");
				    }
					result = name.search(re) >= 0;
					break;
			}
			break;
		case "LastU":
			var updatedDays;
			if (typeof(segment.attributes.updatedOn) === 'undefined' ||  sid === null) {
				updatedDays = 0;
			} else {
				updatedDays =  Math.floor((new Date().getTime() - segment.attributes.updatedOn) / 86400000);
			}
			switch (tree.op) {
				case "0":
					result = parseInt(tree.txt, 10) === updatedDays;
					break;
				case "1":
					result = parseInt(tree.txt, 10) !== updatedDays;
					break;
				case "2":
					result = parseInt(tree.txt, 10) < updatedDays;
					break;
				case "3":
					result = parseInt(tree.txt, 10) <= updatedDays;
					break;
				case "4":
					result = parseInt(tree.txt, 10) > updatedDays;
					break;
				default:
					result = parseInt(tree.txt, 10) >= updatedDays;
					break;
			}
			break;
		case "Length":
			switch (tree.op) {
				case "0":
					result = parseInt(tree.txt, 10) === segment.attributes.length;
					break;
				case "1":
					result = parseInt(tree.txt, 10) !== segment.attributes.length;
					break;
				case "2":
					result = parseInt(tree.txt, 10) < segment.attributes.length;
					break;
				case "3":
					result = parseInt(tree.txt, 10) <= segment.attributes.length;
					break;
				case "4":
					result = parseInt(tree.txt, 10) > segment.attributes.length;
					break;
				default:
					result = parseInt(tree.txt, 10) >= segment.attributes.length;
					break;
			}
			break;
		case "SegId":
			var ids = tree.txt.replace(/[^\d]/g,',');
			var idsArr = ids.split(',');
			switch (tree.op) {
				case "0":
					result = false;
					break;
				default:
					result = true;
					break;
			}
			for(i = 0; i < idsArr.length; i++){
				if (idsArr[i] !== '') { 
					switch (tree.op) {
						case "0":
							result = result || idsArr[i] == segment.attributes.id;
							break;
						default:
							result = result && idsArr[i] != segment.attributes.id;
							break;
					}
				}
			}
			break;
		case "PlaceId":
			var place = Waze.model.venues.objects[tree.txt];
			if (typeof(place) === 'undefined' || place.isPoint()) {
				result = false;
			} else {
				var polygon = place.getPolygonGeometry();
				var points = segment.geometry.getSortedSegments();
				switch (tree.op) {
					case "0":
						result = true;
						break;
					default:
						result = false;
						break;
				}
				for (i = 0; i < points.length; i++) {
					var p1 = new OpenLayers.Geometry.Point(points[i].x1, points[i].y1);
					var p2 = new OpenLayers.Geometry.Point(points[i].x2, points[i].y2);
					switch (tree.op) {
						case "0":
							result = result && polygon.containsPoint(p1) && polygon.containsPoint(p2);
							break;
						default:
							result = result || polygon.containsPoint(p1) || polygon.containsPoint(p2);
							break;
					}
				}
			}
			break;
		case "Bool":
			if (tree.op === "0") {
				result = true;
			} else {
				result = false;
			}
			break;
		case "Edtbl":
			if (tree.op) {
				result = segment.arePropertiesEditable();
			} else {
				result = !segment.arePropertiesEditable();
			}
			break;
		case "And":
			if (checkExpr (tree.L, segment)) {
				result = checkExpr (tree.R, segment);
			} else {
				result = false;
			}
			break;
		case "Or":
			if (checkExpr (tree.L, segment)) {
				result = true;
			} else {
				result = checkExpr (tree.R, segment);
			}
			break;
		case "Not":
			result = !checkExpr (tree.R, segment);
			break;
		case "Bkt":
			result = checkExpr (tree.L, segment);
			break;
		case "Connect":
			result = false;
			switch (tree.op) {
				case "0":
					if (segment.attributes.fromNodeID !== null) {
						var segIDs = Waze.model.nodes.get(segment.attributes.fromNodeID).attributes.segIDs;
						for(var i = 0; i < segIDs.length; i++) {
							if (segIDs[i] !== segment.attributes.id) {
								segment2 = Waze.model.segments.get(segIDs[i]);
								if (typeof(segment2) !== 'undefined') {
									result = checkExpr (tree.L, segment2);
									if (result) {
										break;
									}
								}
							}
						}
					}
					if (!result) {
						if (segment.attributes.toNodeID !== null) {
							var segIDs = Waze.model.nodes.get(segment.attributes.toNodeID).attributes.segIDs;
							for(var i = 0; i < segIDs.length; i++) {
								if (segIDs[i] !== segment.attributes.id) {
									segment2 = Waze.model.segments.get(segIDs[i]);
									if (typeof(segment2) !== 'undefined') {
										result = checkExpr (tree.L, segment2);
										if (result) {
											break;
										}
									}
								}
							}
						}
					}
					break;
				default:
					for(var seg2 in segment.attributes.fromConnections) {
						segment2 = Waze.model.segments.get(seg2);
						if (typeof(segment2) !== 'undefined') {
							result = checkExpr (tree.L, segment2);
							if (result) {
								break;
							}
						}
					}
					if (!result) {
						for(var seg2 in segment.attributes.toConnections) {
							segment2 = Waze.model.segments.get(seg2);
							if (typeof(segment2) !== 'undefined') {
								result = checkExpr (tree.L, segment2);
								if (result) {
									break;
								}
							}
						}
					}
					break;
			}
			break;
		default:
			result = false;
			break;
	}
	return result;
}


function genExptrTxt(tree)
{
	if (typeof (tree.type) === 'undefined') {
		return "";
	}

	var result;
	switch (tree.type) {
		case "Country":
			result = 'Country ' +  EqualOps[tree.op] + ' "' + Countries[tree.id] + '"';
			break;
		case "State":
		case "City":
		case "Street":
		case "ACity":
		case "XCity":
		case "AStreet":
		case "XStreet":
			switch (tree.type) {
				case "ACity":
					result = 'Alt. City ';
					break;
				case "XCity":
					result = 'Prim. or Alt. City ';
					break;
				case "AStreet":
					result = 'Alt. Street ';
					break;
				case "XStreet":
					result = 'Prim. or Alt. Street ';
					break;
				default:
					result = tree.type + ' ';
					break;
			}
			switch (tree.op) {
				case "0":
				case "1":
				case "4":
					result += StringOps[tree.op] + ' "' + tree.txt + '"';
					break;
				default:
					result += StringOps[tree.op] + ' ("' + tree.txt + '")';
					break;
			}
			break;
		case "NoName":
			if (tree.op) {
				result = 'Unnamed';
			} else {
				result = 'Has name';
			}
			break;
		case "ANoName":
			if (tree.op) {
				result = 'NO Alt. names';
			} else {
				result = 'Has Alt. name(s)';
			}
			break;
		case "RoadType":
			result = 'Road Type ' + EqualOps[tree.op] + ' "' + RoadTypes[tree.id] + '"';
			break;
		case "IsToll":
			if (tree.op) {
				result = 'Is Toll Road';
			} else {
				result = 'Is NOT Toll Road';
			}
			break;
		case "IsRound":
			if (tree.op) {
				result = 'Is Roundabout';
			} else {
				result = 'Is NOT Roundabout';
			}
			break;
		case "Direction":
			result = 'Direction ' +  EqualOps[tree.op] + ' "' + Directions[tree.id] + '"';
			break;
		case "Elevation":
			if (tree.id == 0) {
				result = 'Elevation ' + IntegerOps[tree.op] + ' "Ground"';
			} else {
				result = 'Elevation ' + IntegerOps[tree.op] + ' ' + tree.id;
			}
			break;
		case "Tunnel":
			if (tree.op) {
				result = 'Is Tunnel';
			} else {
				result = 'Is NOT Tunnel';
			}
			break;
		case "ManLock":
			result = 'Manual Locks ' + IntegerOps[tree.op] + ' ' + tree.id;
			break;
		case "TrLock":
			result = 'Trafic Locks ' + IntegerOps[tree.op] + ' ' + tree.id;
			break;
		case "Speed":
			if (typeof(tree.op) === "boolean") {
				if (tree.op) {
					tree.op = '1';
					tree.txt = '';
				} else {
					tree.op = '0';
					tree.txt = '';
				}
			}
			if (tree.op === '8') {
				tree.op = '1';
			}
			if (tree.op === '0') {
				result = 'Has NOT speed limit';
			} else if (tree.op !== '6') {
				result = 'Has speed limit';
			} else {
				result = 'Has unverified speed limit';
			}
			if (tree.txt !== '') {
				if (speedInMiles) {
					result += ' ' + km2miles(parseInt(tree.txt, 10)) + ' mph';
				} else {
					result += ' ' + tree.txt + ' km/h';
				}
			}
			if (tree.op !== '6') {
				if (tree.op === '0') {
					if (tree.txt !== '') {
						result += ' in any direction';
					}
				} else {
					result += ' in ' + DirOps[tree.op] + ' direction';
					
					if (tree.op === '2') {
						result += 's';
					}
				}
			}
			break;
		case "SpdC":
			if (tree.op === '0') {
				result = 'Has NOT avg. speed cam';
			} else {
				result = 'Has avg. speed cam';
			}
			if (tree.op === '0') {
				if (tree.txt !== '') {
					result += ' in any direction';
				}
			} else {
				result += ' in ' + DirOps[tree.op] + ' direction';
				if (tree.op === '2') {
					result += 's';
				}
			}
			break;
		case "IsNew":
			if (tree.op) {
				result = 'Is New';
			} else {
				result = 'Is NOT New';
			}
			break;
		case "IsChngd":
			if (tree.op) {
				result = 'Is Changed';
			} else {
				result = 'Is NOT Changed';
			}
			break;
		case "OnScr":
			if (tree.op) {
				result = 'On Screen';
			} else {
				result = 'OUT of Screen';
			}
			break;
		case "Restr":
			if (tree.op) {
				result = 'Has restriction';
			} else {
				result = 'Has NO restriction';
			}
			break;
		case "Clsr":
			if (typeof(tree.op) === "boolean") {
				if (tree.op) {
					result = 'Has closure';
				} else {
					result = 'Has NO closure';
				}
			} else {
				switch (tree.op) {
					case 0:
						result = "Closure starts before";
						break;
					case 1:
						result = "Closure doesn't start before";
						break;
					case 2:
						result = "Closure starts after";
						break;
					case 3:
						result = "Closure doesn't start after";
						break;
					case 4:
						result = "Closure starts in";
						break;
					case 5:
						result = "Closure doesn't start in";
						break;
					case 6:
						result = "Closure ends before";
						break;
					case 7:
						result = "Closure doesn't end before";
						break;
					case 8:
						result = "Closure ends after";
						break;
					case 9:
						result = "Closure doesn't end after";
						break;
					case 10:
						result = "Closure ends in";
						break;
					case 11:
						result = "Closure doesn't end in";
						break;
					default:
						result = "Error";
						break;
				}
				result += ' ' + tree.txt + ' day';
				if (tree.txt !== '1') {
					result += 's';
				}
			}
			break;
		case "ClReas":
			result = 'Closure reason  ';
			switch (tree.op) {
				case "0":
				case "1":
				case "4":
					result += StringOps[tree.op] + ' "' + tree.txt + '"';
					break;
				default:
					result += StringOps[tree.op] + ' ("' + tree.txt + '")';
					break;
			}
			break;
		case "Updtd":
			result = 'Updated by ';
			switch (tree.op) {
				case "0":
				case "1":
				case "4":
					result += StringOps[tree.op] + ' "' + tree.txt + '"';
					break;
				default:
					result += StringOps[tree.op] + ' ("' + tree.txt + '")';
					break;
			}
			break;
		case "Crtd":
			result = 'Created by ';
			switch (tree.op) {
				case "0":
				case "1":
				case "4":
					result += StringOps[tree.op] + ' "' + tree.txt + '"';
					break;
				default:
					result += StringOps[tree.op] + ' ("' + tree.txt + '")';
					break;
			}
			break;
		case "LastU":
			result = 'Last update  ' +	IntegerOps[tree.op] + ' ' + tree.txt + ' days ago';
			break;
		case "Length":
			result = 'Length ' +  IntegerOps[tree.op] + ' ';
			if (speedInMiles) {
				if (tree.txt >= 1609) {
					result += decimalRound(tree.txt/1609.344, -2) + ' miles';
				} else {
					result += Math.round(tree.txt/0.3048) + ' feet';
				}
			} else {
				if (tree.txt >= 1000) {
					result += decimalRound(tree.txt/1000.0, -2) + ' km';
				} else {
					result += tree.txt + ' m';
				}
			}
			break;
		case "SegId":
			result = 'ID ' + StringOps[tree.op] + ' "' + tree.txt + '"';
			break;
		case "PlaceId":
			switch (tree.op) {
				case "0":
					result = 'fully';
					break;
				default:
					result = 'partially';
					break;
			}
			result += ' inside Place ID "' + tree.txt + '"';
			break;
		case "Edtbl":
			if (tree.op) {
				result = 'Is Editable';
			} else {
				result = 'Is NOT Editable';
			}
			break;
		case "Bool":
			if (tree.op === "0") {
				result = 'True';
			} else {
				result = 'False';
			}
			break;
		case "And":
			result = genExptrTxt(tree.L) + ' AND ';
			if (typeof (tree.R) !== 'undefined') {
				result += genExptrTxt(tree.R);
			}
			break;
		case "Or":
			result = genExptrTxt(tree.L) + ' OR ';
			if (typeof (tree.R) !== 'undefined') {
				result += genExptrTxt(tree.R);
			}
			break;
		case "Not":
			result = '! ';
			if (typeof (tree.R) !== 'undefined') {
				result += genExptrTxt(tree.R);
			}
			break;
		case "Bkt":
			result = '(' +  genExptrTxt(tree.L) + ')';
			break;
		case "Connect":
			switch (tree.op) {
				case "0":
					result = 'Connected to (';
					break;
				default:
					result = 'Allowed to (';
					break;
			}
			result += genExptrTxt(tree.L) + ')';
			break;
		default:
			result = "";
			break;
	}
	return result;
}


function displayExpr()
{
	var ExprTxt = "";
	for (var i = 0; i < BktCount; i ++) {
		ExprTxt += genExptrTxt(BktTrees[i]);
		switch (BktOps[i].type) {
			case "Connect":
				switch (BktOps[i].op) {
					case "0":
						ExprTxt += 'Connected to (';
						break;
					default:
						ExprTxt += 'Allowed to (';
						break;
				}
				break;
			default:
				ExprTxt += "(";
				break;
		}
				
	}
	ExprTxt += genExptrTxt(ExprTree);
	getId("outRSExpr").value = ExprTxt;
}


function displayStatus()
{
	if (ExprStatus === 0 || ExprStatus === 2) {
		getId("btnRSAnd").disabled = true;
		getId("btnRSOr").disabled = true;
		if (ExprStatus === 2) {
			getId("btnRSNot").disabled = true;
		} else {
			getId("btnRSNot").disabled = false;
		}
		getId("btnRSRBkt").disabled = true;
		getId("btnRSSelect").disabled = true;
	} else {
		getId("btnRSAnd").disabled = false;
		getId("btnRSOr").disabled = false;
		getId("btnRSNot").disabled = true;
		if (BktCount === 0) {
			getId("btnRSRBkt").disabled = true;
			getId("btnRSSelect").disabled = false;
		} else {
			getId("btnRSRBkt").disabled = false;
			getId("btnRSSelect").disabled = true;
		}
	}
	
	getId("outRSNumBkt").value = BktCount;
	displayExpr();
}


function addCondition(cond)
{
	if (ExprStatus === 1) {
		makeAnd();
	}
	if (typeof (ExprTree.type) === 'undefined') {
		ExprTree = cond;
	} else {
		if (typeof (ExprTree.R) === 'undefined') {
			ExprTree.R = cond;
		} else {
			ExprTree.R.R = cond;
		}
	}
	ExprStatus = 1;
	displayStatus ();
}

	
function makeCountry(ev)
{
	var cond = {};
	cond.type = "Country";
	cond.op = getId("opRSCountry").value;
	cond.id = getId("selRSCountry").value;
	addCondition(cond);
}


function makeState(ev)
{
	var cond = {};
	cond.type = "State";
	cond.op = getId("opRSState").value;
	cond.txt = getId("inRSState").value;
	addCondition(cond);
}


function makeCity(ev)
{
	var cond = {};
	switch (getId("selRSAltCity").value) {
		case "0":
			cond.type = "City";
			break;
		case "1":
			cond.type = "ACity";
			break;
		default:
			cond.type = "XCity";
			break;
	}
	cond.op = getId("opRSCity").value;
	cond.txt = getId("inRSCity").value;
	addCondition(cond);
}


function makeStreet(ev)
{
	var cond = {};
	switch (getId("selRSAlttStreet").value) {
		case "0":
			cond.type = "Street";
			break;
		case "1":
			cond.type = "AStreet";
			break;
		default:
			cond.type = "XStreet";
			break;
	}
	cond.op = getId("opRSStreet").value;
	cond.txt = getId("inRSStreet").value;
	addCondition(cond);
}


function makeNoName(ev)
{
	var cond = {};
	if (getId("cbRSAltNoName").checked) {
		cond.type = "ANoName";
	} else {
		cond.type = "NoName";
	}
	cond.op = getId("cbRSNoName").checked;
	addCondition(cond);
}


function makeRoadType(ev)
{
	var cond = {};
	cond.type = "RoadType";
	cond.op = getId("opRSRoadType").value;
	cond.id = getId("selRSRoadType").value;
	addCondition(cond);
}


function makeIsRound(ev)
{
	var cond = {};
	cond.type = "IsRound";
	cond.op = getId("cbRSIsRound").checked;
	addCondition(cond);
}


function makeIsToll(ev)
{
	var cond = {};
	cond.type = "IsToll";
	cond.op = getId("cbRSIsToll").checked;
	addCondition(cond);
}


function makeDirection(ev)
{
	var cond = {};
	cond.type = "Direction";
	cond.op = getId("opRSDirection").value;
	cond.id = getId("selRSDirection").value;
	addCondition(cond);
}


function makeElevation(ev)
{
	var cond = {};
	cond.type = "Elevation";
	cond.op = getId("opRSElevation").value;
	cond.id = getId("selRSElevation").value;
	addCondition(cond);
}


function makeTunnel(ev)
{
	var cond = {};
	cond.type = "Tunnel";
	cond.op = getId("cbRSTunnel").checked;
	addCondition(cond);
}


function makeManLock(ev)
{
	var cond = {};
	cond.type = "ManLock";
	cond.op = getId("opRSManLock").value;
	cond.id = getId("selRSManLock").value;
	addCondition(cond);
}


function makeTrLock(ev)
{
	var cond = {};
	cond.type = "TrLock";
	cond.op = getId("opRSTrLock").value;
	cond.id = getId("selRSTrLock").value;
	addCondition(cond);
}


function makeSpeed(ev)
{
	var cond = {};
	cond.type = "Speed";
	cond.op = getId("opRSSpeed").value;
	var val = getId("inRSSpeed").value;
	if (isNaN(val) || val === '') {
		cond.txt = '';
	} else {
		val = parseInt(val, 10);
		if (speedInMiles) {
			val = miles2km(val);
		}
		cond.txt = val;
	}
	addCondition(cond);
}


function makeSpdC(ev)
{
	var cond = {};
	cond.type = "SpdC";
	cond.op = getId("opRSSpdC").value;
	addCondition(cond);
}


function makeIsNew(ev)
{
	var cond = {};
	cond.type = "IsNew";
	cond.op = getId("cbRSIsNew").checked;
	addCondition(cond);
}


function makeIsChngd(ev)
{
	var cond = {};
	cond.type = "IsChngd";
	cond.op = getId("cbRSIsChngd").checked;
	addCondition(cond);
}


function makeOnScr(ev)
{
	var cond = {};
	cond.type = "OnScr";
	cond.op = getId("cbRSOnScr").checked;
	addCondition(cond);
}


function makeRestr(ev)
{
	var cond = {};
	cond.type = "Restr";
	cond.op = getId("cbRSRestr").checked;
	addCondition(cond);
}


function makeClsr(ev)
{
	var cond = {};
	cond.type = "Clsr";
	if (parseInt(getId("opRSClsrStrtEnd").value, 10) === 0) {
		cond.op = getId("cbRSClsr").checked;
	} else {
		cond.op = (parseInt(getId("opRSClsrStrtEnd").value, 10) - 1) * 6 + parseInt(getId("opRSClsrBeforeAter").value, 10) * 2 + (getId("cbRSClsr").checked?0:1);
	}
	cond.txt = getId("inRSClsrDays").value;
	addCondition(cond);
}


function makeClReas(ev)
{
	var cond = {};
	cond.type = "ClReas";
	cond.op = getId("opRSClReas").value;
	cond.txt = getId("inRSClReas").value;
	addCondition(cond);
}


function makeUpdtd(ev)
{
	var cond = {};
	cond.type = "Updtd";
	cond.op = getId("opRSUpdtd").value;
	cond.txt = getId("inRSUpdtd").value;
	addCondition(cond);
}


function makeCrtd(ev)
{
	var cond = {};
	cond.type = "Crtd";
	cond.op = getId("opRSCrtd").value;
	cond.txt = getId("inRSCrtd").value;
	addCondition(cond);
}


function makeLastU(ev)
{
	var cond = {};
	cond.type = "LastU";
	cond.op = getId("opRSLastU").value;
	cond.txt = getId("inRSLastU").value;
	if (isNaN(parseInt(cond.txt, 10))) {
		cond.txt = '1';
	}
	addCondition(cond);
}


function makeLength(ev)
{
	var cond = {};
	cond.type = "Length";
	cond.op = getId("opRSLength").value;
	var val = parseFloat(getId("inRSLength").value);
	if (isNaN(val)) {
		cond.txt = 0;
	} else {
		if (speedInMiles) {
			if (getId("unitRSLength").value === '0') {
				val *= 0.3048;
			} else {
				val *= 1609.344;
			}
		} else if (getId("unitRSLength").value === '1') {
			val *= 1000;
		}
		cond.txt = Math.round(val);
	}
	addCondition(cond);
}


function makeSegId(ev)
{
	var cond = {};
	cond.type = "SegId";
	cond.op = getId("opRSSegId").value;
	cond.txt = getId("inRSSegId").value;
	addCondition(cond);
}


function makePlaceId(ev)
{
	var cond = {};
	cond.type = "PlaceId";
	cond.op = getId("opRSPlaceId").value;
	cond.txt = getId("inRSPlaceId").value;
	addCondition(cond);
}


function makeBool(ev)
{
	var cond = {};
	cond.type = "Bool";
	cond.op = getId("opRSBool").value;
	addCondition(cond);
}


function makeEdtbl(ev)
{
	var cond = {};
	cond.type = "Edtbl";
	cond.op = getId("cbRSEdtbl").checked;
	addCondition(cond);
}


function makeAnd(ev)
{
	var op = {};
	op.type = "And";
	if (ExprTree.type === "Or") {
		if (typeof (ExprTree.R) !== 'undefined') {
			op.L = ExprTree.R;
		}
		ExprTree.R = op;
	} else {
		op.L = ExprTree;
		ExprTree = op;
	}
	ExprStatus = 0;
	displayStatus ();
}


function makeOr(ev)
{
	var op = {};
	op.type = "Or";
	op.L = ExprTree;
	ExprTree = op;
	ExprStatus = 0;
	displayStatus ();
}


function makeNot(ev)
{
	var op = {};
	op.type = "Not";
	if (typeof (ExprTree.type) === 'undefined') {
		ExprTree = op;
	} else {
		if (typeof (ExprTree.R) === 'undefined') {
			ExprTree.R = op;
		} else {
			ExprTree.R.R = op;
		}
	}
	ExprStatus = 2;
	displayStatus ();
}


function makeLBkt(ev)
{
	if (ExprStatus === 1) {
		makeAnd();
	}
	BktTrees[BktCount] = ExprTree;
	var cond = {};
	cond.type = "Bkt";
	BktOps[BktCount] = cond;
	ExprTree = new Object;
	BktCount ++;
	ExprStatus = 0;
	displayStatus ();
}


function makeConnect(ev)
{
	if (ExprStatus === 1) {
		makeAnd();
	}
	BktTrees[BktCount] = ExprTree;
	var cond = {};
	cond.type = "Connect";
	cond.op = getId("opRSConnect").value;
	BktOps[BktCount] = cond;
	ExprTree = new Object;
	BktCount ++;
	ExprStatus = 0;
	displayStatus ();
}


function makeRBkt(ev)
{
	BktCount --;
	var cond = BktOps[BktCount];
	cond.L = ExprTree;
	ExprTree = BktTrees[BktCount];
	ExprStatus = 0;
	addCondition(cond);
}


function selectRoads()
{
	getId("btnRSSelect").disabled = true;
	getId("btnRSSelect").style.background='#F00000';
	
	setTimeout(selectRoads2, 100);
}


function selectRoads2()
{
	var foundSegs = [];
	var selected = 0;
	var max_sel_txt = getId("inRSLimit").value;
	var max_sel = parseInt(max_sel_txt, 10);

	 for (var seg in Waze.model.segments.objects) {
		 var segment = Waze.model.segments.get(seg);
		 if (segment.arePropertiesEditable() || !getId("cbRSEditable").checked) {
			if (checkExpr(ExprTree, segment)) {
				foundSegs.push(segment);
				selected++;
				if ((getId("cbRSLimit").checked) && (selected === max_sel)) {
					break;
				}
			}
		}
	}
	Waze.selectionManager.select(foundSegs);
	
	getId("btnRSSelect").style.background='#E9E9E9';
	getId("btnRSSelect").disabled = false;
}


function clearExpr(ev)
{
	ExprStatus = 0;
	ExprTree = {};
	BktCount = 0;
	displayStatus ();
}

function delLast(ev)
{
	if (typeof (ExprTree.type) === 'undefined') {
		if (BktCount > 0) {
			BktCount--;
			ExprTree = BktTrees[BktCount];
			if (ExprTree.type === "Not") {
				ExprStatus = 2;
			} else {
				ExprStatus = 0;
			}
		}
	} else {
		switch (ExprTree.type) {
			case "And":
				if (typeof (ExprTree.R) === 'undefined') {
					ExprTree = ExprTree.L;
					ExprStatus = 1;
				} else {
					if (ExprTree.R.type === "Bkt") {
						BktTrees[BktCount] = ExprTree;
						ExprTree = ExprTree.R.L;
						delete BktTrees[BktCount].R;
						BktCount ++;
						ExprStatus = 1;
					} else if (ExprTree.R.type === "Not") {
						if (typeof (ExprTree.R.R) === 'undefined') {
							delete ExprTree.R;
							ExprStatus = 0;
						} else {
							delete ExprTree.R.R;
							ExprStatus = 2;
						}
					} else {
						delete ExprTree.R;
						ExprStatus = 0;
					}
				}
				break;
			case "Or":
				if (typeof (ExprTree.R) === 'undefined') {
					ExprTree = ExprTree.L;
					ExprStatus = 1;
				} else {
					if (ExprTree.R.type === "Bkt") {
						BktTrees[BktCount] = ExprTree;
						ExprTree = ExprTree.R.L;
						delete BktTrees[BktCount].R;
						BktCount ++;
						ExprStatus = 1;
					} else if (ExprTree.R.type === "Not") {
						if (typeof (ExprTree.R.R) === 'undefined') {
							delete ExprTree.R;
							ExprStatus = 0;
						} else {
							delete ExprTree.R.R;
							ExprStatus = 2;
						}
					} else if (ExprTree.R.type === "And") {
						if (typeof (ExprTree.R.R) === 'undefined') {
							ExprTree.R = ExprTree.R.L;
							ExprStatus = 1;
						} else {
							delete ExprTree.R.R;
							ExprStatus = 0;
						}
					} else {
						delete ExprTree.R;
						ExprStatus =  0;
					}
				}
				break;
			case "Bkt":
			case "Connect":
				BktTrees[BktCount] = {};
				ExprTree = ExprTree.L;
				BktCount ++;
				ExprStatus = 1;
				break;
			default:
				ExprTree = {};
				ExprStatus = 0;
				break;
		}
	}
	displayStatus ();
}


var icon_delete    ="iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3ggaDwcglEfa9wAAAftJREFUOMvNks1qFEEUhb+q7uqedNMdTJwJdjBRdBYG/4MEsgiGBAIuBBF8D8GXcO/LuHAjQkQJoglOFiN0YH4MKBomM3a6a6rcTOvEyQNYUHA5de5368CF/+Z0CABoU6m3qdRL7a/u19v49XEvgBiHdJ0okUo1AExRXLsw7HUAuipOpF9p4EjMcf+PDiDLooVXla67X1tdiWqrK5FwZKOFWmghF6TyGrUnj6PaveVIuu5+C686ARDIEPDRQ0x/IKp3bsfKD7dVOLtdfbAVm88NQZ4D+CPvZISOCNek6748f+tGBUAIgaxfZdhsgrF8+7ibGa23Ett/PQHoEJAwoE1l2VH+q9mb16etMWAtwnH4/mnvaFicbMyT7ZTeUxESBnQImCfbkWHYtkWBLQqs1litAdr/Np8ClA+HYbU7u762NMwyjLVYaxme5Exfubz01Y275aCJCCl4U9HcQe3Rw7nB23cCY+ilBz8sEF9aPAcglbJHzeZhURwvXsTkp36g8BLhyJlf73cQ1tpemmZG5xtW5xu9NM2EENZqDZYZiZ+cuY2pCO533TjrunG2J4LN0QC5K4LNUv8igvXxnjKCkDBlIHqBt55h1TOKD4A38uTPUXc9hH5K/kbAwMJPoBBjIAVEgA+4o+YyogX02O0DA0CLM5KIs5ZsBJmofwPkNtFJWyXQAQAAAABJRU5ErkJggg==";

function loadCss() {
	var cssEl = document.createElement("style");
	cssEl.type = "text/css";
	var css = ".divRSRow { clear:both; line-height:16px; height: 20px; text-indent:5px; border: 1px solid;  border-top:0;}";
	css += ".divRSRow0 { clear:both; line-height:16px; height: 20px; text-indent:5px; border: 1px solid; }";
	css += ".divRSName { float:left; text-align:left; padding-left:0px; width:240px; }";
	css += ".divRSDel { float:right; width:20px; padding-top :1px; }";
	cssEl.innerHTML = css;
	document.body.appendChild(cssEl);
}

function genSavedHTML ()
{
	getId('RSsavedTable').innerHTML="";
	for (var i = 0; i < SavedQueries.length; i++) {
		var savedRow = document.createElement('div');
		if (i == 0) {
			savedRow.className="divRSRow0";
		} else {
			savedRow.className="divRSRow";
		}
		savedRow.id="RSsavedRow_"+i;
		
		var namediv = document.createElement('div');
		namediv.className="divRSName";
		
		var namea = document.createElement('a');
		namea.innerHTML="<b>"+SavedQueries[i].name+"</b>";
		namea.href = "#";
		namea.onclick = getFunctionWithArgs(selectRow, [i]);
		namediv.appendChild(namea);
		savedRow.appendChild(namediv);
		
		var deletediv = document.createElement('div');
		deletediv.className="divRSDel";
		var deletea = document.createElement('a');
		deletea.innerHTML="<img title='delete' src='data:image/png;base64," +icon_delete +"' />"; 
		deletea.href = "#";
		deletea.onclick = getFunctionWithArgs(deleteRow, [i]);
		deletediv.appendChild(deletea);
		savedRow.appendChild(deletediv); 

		getId('RSsavedTable').appendChild(savedRow);
	}
}

var SavedQueries = [];

function makeSave(ev)
{
	var name = getId("inRSSaveName").value.substring(0,40);
	var query = {
		name: name,
		ExprStatus: ExprStatus,
		ExprTree: clone(ExprTree),
		BktTrees: clone(BktTrees),
		BktOps: clone(BktOps),
		BktCount: BktCount
	};
	var done = false;
	for (var i = 0; i < SavedQueries.length; i++) {
		if (SavedQueries[i].name === name) {
			SavedQueries[i] = query;
			done = true;
			break;
		}
		if (SavedQueries[i].name > name) {
			SavedQueries.splice(i, 0, query);
			done = true;
			break;
		}
	}
	if (!done) {
		SavedQueries.push(query);
	}
	localStorage.WMERoadSelector = JSON.stringify(SavedQueries);
	genSavedHTML();
}


function selectRow(id)
{
	ExprStatus = SavedQueries[id].ExprStatus;
	ExprTree = clone(SavedQueries[id].ExprTree);
	BktTrees = clone(SavedQueries[id].BktTrees);
	BktCount = SavedQueries[id].BktCount;
	if (typeof(SavedQueries[id].BktOps) !== "undefined") {
		BktOps = clone(SavedQueries[id].BktOps);
	} else {
		BktOps = {};
		for (var i = 0; i < BktCount; i++) {
			var cond = {};
			cond.type = "Bkt";
			BktOps[i] = cond;
		}
	}
	getId("inRSSaveName").value = SavedQueries[id].name;
	displayStatus ();
}


function deleteRow(id)
{
	SavedQueries.splice(id, 1);
	localStorage.WMERoadSelector = JSON.stringify(SavedQueries);
	genSavedHTML();
}


function getElementsByClassName(classname, node)
{
	if(!node) {
		node = document.getElementsByTagName("body")[0];
	}
	var a = [];
	var re = new RegExp('\\b' + classname + '\\b');
	var els = node.getElementsByTagName("*");
	for (var i=0,j=els.length; i<j; i++) {
		if (re.test(els[i].className)) {
			 a.push(els[i]);
		 }
	 }
	return a;
}


function getFunctionWithArgs(func, args) {
    return (
        function () {
            var json_args = JSON.stringify(args);
            return function() {
                var args = JSON.parse(json_args);
                func.apply(this, args);
            };
        }
    )();
}


function getId(node)
{
	return document.getElementById(node);
}


function clone(obj) {
	var copy;
	if (null == obj || "object" != typeof obj) return obj;
	if (obj instanceof Date) {
		copy = new Date();
		copy.setTime(obj.getTime());
		return copy;
	}
	if (obj instanceof Array) {
		copy = [];
		for (var i = 0, len = obj.length; i < len; i++) {
			copy[i] = clone(obj[i]);
		}
		return copy;
	}
	if (obj instanceof Object) {
		copy = {};
		for (var attr in obj) {
			if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
		}
		return copy;
	}
    throw new Error("Unable to copy obj! Its type isn't supported.");
}


function roadSelector_init()
{
	if (localStorage.WMERoadSelector) {
		SavedQueries = JSON.parse(localStorage.WMERoadSelector);
	}
	loadCss();

	var addon = document.createElement('section');

	var lang = unsafeWindow.I18n.locale;
	if (lang === 'cs' || lang === 'sk') {
		addon.innerHTML  = '<b><u><a href="https://www.waze.com/forum/viewtopic.php?f=22&t=101365" target="_blank">WME Road Selector</a></u></b> &nbsp; v' + GM_info.script.version;
	} else {
		addon.innerHTML  = '<b><u><a href="https://www.waze.com/forum/viewtopic.php?f=819&t=112497" target="_blank">WME Road Selector</a></u></b> &nbsp; v' + GM_info.script.version;
	}

	var section = document.createElement('p');
	section.style.paddingTop = "8px";
	section.style.textIndent = "16px";
	section.style.fontSize = "10px";
	section.style.textIndent = "0px";
	section.id = "RSselection";
	section.innerHTML  = '<font size=3>Selection</font><br>'
						+ '<output id="outRSExpr"></output><br>'
						+ '<input type="checkbox" id="cbRSEditable" style="padding:0px 0px; height:20px" checked /><b>   Editable only</b>&nbsp;&nbsp;'
						+ '<input type="checkbox" id="cbRSCaseSens" style="padding:0px 0px; height:20px" checked/><b>Case sensitive</b><br>'
						+ '<input type="checkbox" id="cbRSLimit" style="padding:0px 0px; height:20px" /><b>Limit # of segs</b>&nbsp;'
						+ '<input type="number" min="1" max="999" value="100" id="inRSLimit" size=3 style="padding:0px 0px; height:20px;  width:50px; display:inline-block"/><br><br>'
						+ '<button class="btn btn-default" id="btnRSSelect" style="padding:0px 10px; height:30px">   Select roads   </button>&nbsp;&nbsp;&nbsp;'
						+ '<button class="btn btn-default" id="btnRSClear" style="padding:0px 10px; height:20px">  Clear   </button>&nbsp;&nbsp;&nbsp;'
						+ '<button class="btn btn-default" id="btnRSDel" style="padding:0px 10px; height:20px">  Delete last   </button>'
					 ;
	addon.appendChild(section);

	var tabs = document.createElement("ul");
	addon.appendChild(tabs);
	tabs.id = "roadselector-tabs";
	tabs.className = "nav nav-tabs";
	tabs.innerHTML = '<li class="active" style="width: 50%; text-align: center; height: 30px;"><a style="height: 30px;" href="#roadselector-tabs-editor" data-toggle="tab">Editor</a></li>';
	tabs.innerHTML += '<li class="" style="width: 50%; text-align: center; height: 30px;"><a style="height: 30px;" href="#roadselector-tabs-saved" data-toggle="tab">Saved</a></li>';
	tabcont = document.createElement("div");
	tabcont.className = "tab-content";
	tabcont.id = "roadselector-tab-content";
	tabcont.style.paddingBottom = "10px";
	tabcont.style.paddingTop = "10px";
	tabcont.style.paddingLeft = "5px";
	tabcont.style.paddingRight = "5px";
	addon.appendChild(tabcont);
	var tabpane = document.createElement("section");
	tabpane.className = "tab-pane active";
	tabpane.id = "roadselector-tabs-editor";
	tabcont.appendChild(tabpane);

	section = document.createElement('p');
	section.style.paddingTop = "8px";
	section.style.textIndent = "16px";
	section.style.fontSize = "10px";
	section.style.textIndent = "0px";
	section.id = "RSoperations";
	section.innerHTML  = '<font size=3>Operations</font><br>'
						+ '<button class="btn btn-default" id="btnRSAnd" style="padding:0px 10px; height:20px">AND</button>&nbsp;'
						+ '<button class="btn btn-default" id="btnRSOr" style="padding:0px 10px; height:20px">OR</button>&nbsp;'
						+ '<button class="btn btn-default" id="btnRSNot" style="padding:0px 10px; height:20px"> ! </button>&nbsp;'
						+ '<button class="btn btn-default" id="btnRSLBkt" style="padding:0px 10px; height:20px"> ( </button>&nbsp;'
						+ '<output id="outRSNumBkt" style="padding:0px 0px; height:20px; display:inline-block">0</output>&nbsp;'
						+ '<button class="btn btn-default" id="btnRSRBkt" style="padding:0px 10px; height:20px"> ) </button>'
					 ;
	tabpane.appendChild(section);

	section = document.createElement('p');
	section.style.paddingTop = "8px";
	section.style.textIndent = "16px";
	section.style.fontSize = "9px";
	section.style.textIndent = "0px";
	section.id = "RSconditions";
	str  = '<font size=3>Conditions</font>'
					 + '<table width=100% rules=rows>'
					 + '<tr><td><b>Country</b>&nbsp;<select id="opRSCountry" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<select id="selRSCountry" style="padding:0px 0px; height:20px"></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddCountry" style="padding:0px 10px; height:20px">Add</button></td></tr>';
	if (hasStates) {
		str  += '<tr><td><b>State</b>&nbsp;<select id="opRSState" style="padding:0px 0px; height:20px"></select>&nbsp;'
							+ '<input  type="text" id="inRSState" size=10 style="padding:0px 0px; height:20px" /></td>'
							+ '<td><button class="btn btn-default" id="btnRSAddState" style="padding:0px 10px; height:20px">Add</button></td></tr>';
	}
	str  += '<tr><td><b>City</b>&nbsp;<select id="opRSCity" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input  type="text" id="inRSCity" size=10 style="padding:0px 0px; height:20px" />&nbsp;&nbsp;'
						+ '<select id="selRSAltCity" style="padding:0px 0px; height:20px"><option value="0">prim.</option><option value="1">alt.</option><option value="2">any</option></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddCity" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Street</b>&nbsp;<select id="opRSStreet" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input type="text" id="inRSStreet" size=9 style="padding:0px 0px; height:20px" />&nbsp;&nbsp;'
						+ '<select id="selRSAlttStreet" style="padding:0px 0px; height:20px"><option value="0">prim.</option><option value="1">alt.</option><option value="2">any</option></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddStreet" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Unnamed segment</b>&nbsp;<input type="checkbox" id="cbRSNoName" style="padding:0px 0px" checked />&nbsp;&nbsp;<b>alt.</b>&nbsp;<input type="checkbox" id="cbRSAltNoName" style="padding:0px 0px" /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddNoName" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Road type</b>&nbsp;<select id="opRSRoadType" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<select id="selRSRoadType" style="padding:0px 0px; height:20px"></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddRoadType" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Roundabout</b>&nbsp;<input type="checkbox" id="cbRSIsRound" style="padding:0px 0px" checked /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddIsRound" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Toll Road</b>&nbsp;<input type="checkbox" id="cbRSIsToll" style="padding:0px 0px" checked /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddIsToll" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Direction</b>&nbsp;<select id="opRSDirection" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<select id="selRSDirection" style="padding:0px 0px; height:20px"></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddDirection" style="padding:0px 10px; height:20px">Add</button>'
					 + '<tr><td><b>Elevation</b>&nbsp;<select id="opRSElevation" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<select id="selRSElevation" style="padding:0px 0px; height:20px"></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddElevation" style="padding:0px 10px; height:20px">Add</button>'
					 + '<tr><td><b>Tunnel</b>&nbsp;<input type="checkbox" id="cbRSTunnel" style="padding:0px 0px" checked /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddTunnel" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Manual Lock</b>&nbsp;<select id="opRSManLock" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<select id="selRSManLock" style="padding:0px 0px; height:20px"></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddManLock" style="padding:0px 10px; height:20px">Add</button>'
					 + '<tr><td><b>Trafic Lock</b>&nbsp;<select id="opRSTrLock" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<select id="selRSTrLock" style="padding:0px 0px; height:20px"></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddTrLock" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Speed limit</b>&nbsp;<select id="opRSSpeed" style="padding:0px 0px; height:20px"></select> <b>dir.</b> '
						+ '<input  type="text" id="inRSSpeed" size=4 style="padding:0px 0px; height:20px" /> <b>' + (speedInMiles?'mph':'km/h') +'</b></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddSpeed" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Avg. speed cam</b>&nbsp;<select id="opRSSpdC" style="padding:0px 0px; height:20px"></select> <b>dir.</b> '
						+ '<td><button class="btn btn-default" id="btnRSAddSpdC" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>New</b>&nbsp;<input type="checkbox" id="cbRSIsNew" style="padding:0px 0px" checked /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddIsNew" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Changed</b>&nbsp;<input type="checkbox" id="cbRSIsChngd" style="padding:0px 0px" checked /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddIsChngd" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>On Screen</b>&nbsp;<input type="checkbox" id="cbRSOnScr" style="padding:0px 0px" checked /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddOnScr" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Has restriction</b>&nbsp;<input type="checkbox" id="cbRSRestr" style="padding:0px 0px" checked /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddRestr"" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Has closure</b>&nbsp;<input type="checkbox" id="cbRSClsr" style="padding:0px 0px" checked />&nbsp;'
						+ '<select id="opRSClsrStrtEnd" style="padding:0px 0px; height:20px"><option value="0">---</option><option value="1">starts</option><option value="2">ends</option></select><br>'
						+ '<select id="opRSClsrBeforeAter" style="padding:0px 0px; height:20px"><option value="0">before</option><option value="1">after</option><option value="2">in</option></select>&nbsp;'
						+ '<input  type="number" min="-365" max="365" value="1" id="inRSClsrDays" size=3 style="padding:0px 0px; height:20px;  width:50px; display:inline-block"/>&nbsp;<b>days</b></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddClsr"" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Closure reason</b>&nbsp;<select id="opRSClReas" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input type="text" id="inRSClReas" size=9 style="padding:0px 0px; height:20px" /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddClReas" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Updated by</b>&nbsp;<select id="opRSUpdtd" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input  type="text" id="inRSUpdtd" size=8 style="padding:0px 0px; height:20px" /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddUpdtd" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Created by</b>&nbsp;<select id="opRSCrtd" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input  type="text" id="inRSCrtd" size=8 style="padding:0px 0px; height:20px" /></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddCrtd" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Last update</b>&nbsp;<select id="opRSLastU" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input  type="number" min="0" max="365" value="1" id="inRSLastU" size=3 style="padding:0px 0px; height:20px;  width:50px; display:inline-block"/>&nbsp;<b> days ago</b></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddLastU" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Length</b>&nbsp;<select id="opRSLength" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input  type="text" id="inRSLength" size=10 style="padding:0px 0px; height:20px; display:inline-block"/>&nbsp;'
						+ '<select id="unitRSLength" style="padding:0px 0px; height:20px"><option value="0">' + (speedInMiles?'feet':'m') + '</option><option value="1">' + (speedInMiles?'miles':'km') + '</option></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddLength" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><select id="opRSConnect" style="padding:0px 0px; height:20px"><option value="0">Connected</option><option value="1">Allowed</option></select><b> to ( ... )</b></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddConnect" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>ID</b>&nbsp;<select id="opRSSegId" style="padding:0px 0px; height:20px"></select>&nbsp;'
						+ '<input type="text" id="inRSSegId" size=25 style="padding:0px 0px; height:20px"/></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddSegId" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					+ '<tr><td><select id="opRSPlaceId" style="padding:0px 0px; height:20px"><option value="0">fully</option><option value="1">partially</option></select>&nbsp;<b>inside Place ID</b>&nbsp;'
						+ '<input type="text" id="inRSPlaceId" style="padding:0px 0px; height:20px;  width:80px; display:inline-block"/></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddPlaceId" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><select id="opRSBool" style="padding:0px 0px; height:20px"><option value="0">True</option><option value="1">False</option></select></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddBool" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '<tr><td><b>Editable</b>&nbsp;<input type="checkbox" id="cbRSEdtbl" style="padding:0px 0px"/></td>'
						+ '<td><button class="btn btn-default" id="btnRSAddEdtbl" style="padding:0px 10px; height:20px">Add</button></td></tr>'
					 + '</table>';
	section.innerHTML = str;
	tabpane.appendChild(section);

	tabpane = document.createElement("section");
	tabpane.className = "tab-pane";
	tabpane.id = "roadselector-tabs-saved";
	tabcont.appendChild(tabpane);

	section = document.createElement('p');
	section.style.paddingTop = "8px";
	section.style.textIndent = "16px";
	section.style.fontSize = "10px";
	section.style.textIndent = "0px";
	section.id = "RSsavedHdr";
	section.innerHTML  = '<table width=100% rules=rows>'
					 + '<tr><td><input type="text" id="inRSSaveName" size=30 style="padding:0px 0px; height:20px" /></td>'
					 + '<td><button class="btn btn-default" id="btnRSSave" style="padding:0px 10px; height:20px">Save</button></td></tr>'
					 + '</table>';
	tabpane.appendChild(section);

	section = document.createElement('p');
	section.style.paddingTop = "8px";
	section.style.textIndent = "16px";
	section.style.fontSize = "10px";
	section.style.textIndent = "0px";
	section.id = "RSsaved";
	var savedTable = "<div id='RSsavedTable'></div>";
	section.innerHTML  = savedTable;
	tabpane.appendChild(section);

	var userTabs = getId('user-info');
	var navTabs = getElementsByClassName('nav-tabs', userTabs)[0];
	var tabContent = getElementsByClassName('tab-content', userTabs)[0];

	var newtab = document.createElement('li');
	newtab.innerHTML = '<a href="#sidepanel-roadselector" data-toggle="tab" title="Road Selector">RSel</a>';
	navTabs.appendChild(newtab);

	addon.id = "sidepanel-roadselector";
	addon.className = "tab-pane";
	tabContent.appendChild(addon);

	genSavedHTML();

	populateEqualOps("opRSCountry");
	populateCountries("selRSCountry");
	if (hasStates) {
		populateStringOps("opRSState");
	}
	populateStringOps("opRSCity");
	populateStringOps("opRSStreet");
	populateEqualOps("opRSRoadType");
	populateRoadTypes("selRSRoadType");
	populateEqualOps("opRSDirection");
	populateDirections("selRSDirection");
	populateIntegerOps("opRSElevation");
	populateElevations("selRSElevation");
	populateIntegerOps("opRSManLock");
	populateLocks("selRSManLock");
	populateIntegerOps("opRSTrLock");
	populateLocks("selRSTrLock");
	populateDirOps("opRSSpeed", true);
	populateDirOps("opRSSpdC", false);
	populateStringOps("opRSClReas");
	populateStringOps("opRSUpdtd");
	populateStringOps("opRSCrtd");
	populateIntegerOps("opRSLastU");
	populateEqualOps("opRSSegId");
	populateIntegerOps("opRSLength");

	displayStatus ();


	getId("btnRSAddCountry").onclick = makeCountry;
	if (hasStates) {
		getId("btnRSAddState").onclick = makeState;
	}
	getId("btnRSAddCity").onclick = makeCity;
	getId("btnRSAddStreet").onclick = makeStreet;
	getId("btnRSAddNoName").onclick = makeNoName;
	getId("btnRSAddRoadType").onclick = makeRoadType;
	getId("btnRSAddIsRound").onclick = makeIsRound;
	getId("btnRSAddIsToll").onclick = makeIsToll;
	getId("btnRSAddDirection").onclick = makeDirection;
	getId("btnRSAddElevation").onclick = makeElevation;
	getId("btnRSAddTunnel").onclick = makeTunnel;
	getId("btnRSAddManLock").onclick = makeManLock;
	getId("btnRSAddTrLock").onclick = makeTrLock;
	getId("btnRSAddSpeed").onclick = makeSpeed;
	getId("btnRSAddSpdC").onclick = makeSpdC;
	getId("btnRSAddIsNew").onclick = makeIsNew;
	getId("btnRSAddIsChngd").onclick = makeIsChngd;
	getId("btnRSAddOnScr").onclick = makeOnScr;
	getId("btnRSAddRestr").onclick = makeRestr;
	getId("btnRSAddClsr").onclick = makeClsr;
	getId("btnRSAddClReas").onclick = makeClReas;
	getId("btnRSAddUpdtd").onclick = makeUpdtd;
	getId("btnRSAddCrtd").onclick = makeCrtd;
	getId("btnRSAddLastU").onclick = makeLastU;
	getId("btnRSAddLength").onclick = makeLength;
	getId("btnRSAddConnect").onclick = makeConnect;
	getId("btnRSAddSegId").onclick = makeSegId;
	getId("btnRSAddPlaceId").onclick = makePlaceId;
	getId("btnRSAddBool").onclick = makeBool;
	getId("btnRSAddEdtbl").onclick = makeEdtbl;
	getId("btnRSAnd").onclick = makeAnd;
	getId("btnRSOr").onclick = makeOr;
	getId("btnRSNot").onclick = makeNot;
	getId("btnRSLBkt").onclick = makeLBkt;
	getId("btnRSRBkt").onclick = makeRBkt;
	getId("btnRSSelect").onclick = selectRoads;
	getId("btnRSClear").onclick = clearExpr;
	getId("btnRSDel").onclick = delLast;
	getId("btnRSSave").onclick = makeSave;
	
	if (SavedQueries.length > 0) {
		$('#roadselector-tabs a[href="#roadselector-tabs-saved"]').tab('show');
	}

	unsafeWindow.RoadSelector = {
		checkSegment: function(expression, segment) {
			if (typeof(expression) === 'undefined' ||  expression === null) {
				return false;
			} else {
				return checkExpr(expression, segment);
			}
		},
		getCurrentExpression: function() {
			if (ExprStatus === 1 && BktCount === 0) {
				var e = clone(ExprTree);
				return e;
			} else {
				return null;
			}
		},
		getSavedNames: function() {
			var lst = [];
			for (var i = 0; i < SavedQueries.length; i++) {
				if (SavedQueries[i].ExprStatus === 1) {
					lst.push(SavedQueries[i].name);
				}
			}
			return lst;
		},
		getSavedExpression: function(name) {
			for (var i = 0; i < SavedQueries.length; i++) {
				if (SavedQueries[i].ExprStatus === 1 && SavedQueries[i].BktCount === 0 && SavedQueries[i].name === name) {
					 var e = clone(SavedQueries[i].ExprTree);
					 return e;
				}
				
			}
			return null;
		},
		getExpressionText: function(expression) {
			if (typeof(expression) === 'undefined' ||  expression === null) {
				return 'Wrong expression!';
			} else {
				return genExptrTxt(expression);
			}
		}
	};

}


function roadSelector_bootstrap()
{
	if ((typeof(Waze) === 'undefined') || (typeof(Waze.model) === 'undefined') || (typeof(Waze.model.countries.objects) === 'undefined') || (Object.keys(Waze.model.countries.objects).length === 0)) {
		setTimeout(roadSelector_bootstrap, 500);
	} else {
		hasStates = Waze.model.hasStates();
		speedInMiles = Waze.model.isImperial;
		roadSelector_init();
	}
}


roadSelector_bootstrap();