// ==UserScript==
// @name WME Bad Junction Angle Info
// @description Shows "Bad Angle Infos" of all Junctions in the editing area
// @match https://beta.waze.com/*editor*
// @match https://www.waze.com/*editor*
// @exclude https://www.waze.com/*user/*editor/*
// @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @version 1.9.13
// @grant GM_addElement
// @namespace https://wms.kbox.at/
// @copyright 2021 Gerhard; 2018 seb-d59, 2016 Michael Wikberg <waze@wikberg.fi>
// @license CC-BY-NC-SA
// @icon data:image/png;base64,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
// ==/UserScript==
/**
* This script is based on the code from "WME Junction angle Info".
* Thanks for the great work to the authors of the original script:
*/
/**
* Copyright 2016 Michael Wikberg <waze@wikberg.fi>
* WME Junction Angle Info extension is licensed under a Creative Commons
* Attribution-NonCommercial-ShareAlike 3.0 Unported License.
*
* Contributions by:
* 2014 Paweł Pyrczak "tkr85" <support@pyrczak.pl>
* 2014 "AlanOfTheBerg" <alanoftheberg@gmail.com>
* 2014 "berestovskyy" <?>
* 2015 "FZ69617" <?>
* 2015 "wlodek76" <?>
* 2016 Sergey Kuznetsov "WazeRus" <sergey@izhevsk.pro> (Russian translation)
* 2016 "MajkiiTelini" <?> Czech translation
* 2016 "witoco" <?> (Latin-American Spanish translation)
* 2017 "seb-d59" (Check override instruction and French translation) <https://www.waze.com/forum/memberlist.php?mode=viewprofile&u=16863068>
* 2019 thank to Sapozhnik for the Ukrainian (український) translation
*/
/*jshint eqnull:true, nonew:true, nomen:true, curly:true, latedef:true, unused:strict, noarg:true, loopfunc:true */
/*jshint trailing:true, forin:true, noempty:true, maxparams:7, maxerr:100, eqeqeq:true, strict:true, undef:true */
/*jshint bitwise:true, newcap:true, immed:true, onevar:true, browser:true, nonbsp:true, freeze:true */
/*global I18n, $*/
/* global _ */
/* global W */
/* global WazeWrap */
function run_aja() {
"use strict";
// var TURN_ANGLE = 45.50; //Turn vs. keep angle - based on map experiments (45.04 specified in Wiki).
// var GRAY_ZONE = 1.5; //Gray zone angle intended to prevent from irregularities observed on map.
/*
* First some variable and enumeration definitions
*/
var junctionangle_version = "1.9.13";
var name = "Bad Junction Angle Info";
const AJA_UPDATE_NOTES = `<b>FIX:</b><br>
- Update for new Beta WME<br>`;
var junctionangle_debug = 0; //0: no output, 1: basic info, 2: debug 3: verbose debug, 4: insane debug
var aja_last_restart = 0;
var aja_roundabout_points = [];
var aja_options = {};
var aja_mapLayer;
// var scriptenabled = true;
var pointSize = 12;
var decimals = 2;
var AJASettings = {};
var country;
var withRouting = true;
var aja_vehicle_types = {
TRUCK: 1,
PUBLIC: 2,
TAXI: 4,
BUS: 8,
HOV2: 16,
HOV3: 32,
RV: 64,
TOWING: 128,
MOTORBIKE: 256,
PRIVATE: 512,
HAZ: 1024
};
const css = [
'.aja-wrapper {position:relative;width:100%;font-size:12px;font-family:"Rubik", "Boing-light", sans-serif;user-select:none;}',
'.aja-section-wrapper {display:block;width:100%;padding:4px;}',
'.aja-section-wrapper.border {border-bottom:1px solid grey;margin-bottom:5px;}',
'.aja-header {font-weight:bold;}',
'.aja-option-container {padding:3px;}',
'.aja-option-container.no-display {display:none;}',
'.aja-option-container.sub {margin-left:20px;}',
'input[type="checkbox"].aja-checkbox {display:inline-block;position:relative;top:3px;vertical-align:top;margin:0;}',
'input[type="color"].aja-color-input {display:inline-block;position:relative;width:20px;padding:0px 1px;border:0px;vertical-align:top;cursor:pointer;}',
'input[type="color"].aja-color-input:focus {outline-width:0;}',
'label.aja-label {display:inline-block;position:relative;max-width:80%;vertical-align:top;font-weight:normal;padding-left:5px;word-wrap:break-word;}',
'.group-title.toolbar-top-level-item-title.rsa:hover {cursor:pointer;}'
].join(' ');
const TRANSLATIONS = {
default: {
scriptTitle: 'Bad Junction Angle Info',
scriptenabled: 'Script enabled',
check: 'Check for TIOs and Restrictions',
skiproundabout: 'Skip Roundabouts'
}
};
/*
* Main logic functions
*/
function junctionangle_init() {
// Register event listeners
WazeWrap.Events.register('selectionchanged', null, aja_calculate);
WazeWrap.Events.register('moveend', null, aja_calculate);
WazeWrap.Events.register('afteraction', null, aja_calculate);
WazeWrap.Events.register('moveend', null, aja_calculate);
window.W.model.segments.on({
"objectschanged": aja_calculate,
"objectsremoved": aja_calculate
});
window.W.model.nodes.on({
"objectschanged": aja_calculate,
"objectsremoved": aja_calculate
});
window.W.map.olMap.events.register("zoomend", null, aja_calculate);
window.W.map.olMap.events.register("move", null, aja_calculate);
const $section = $('<div>');
// HTML for UI tab
$section.html([
`<div class='aja-wrapper' id='aja-tab-wrapper'>
<div style='margin-bottom:5px;border-bottom:1px solid black;'>
<span style='font-weight:bold;'>
<a href='https://www.waze.com/forum/viewtopic.php?t=334486' target='_blank' style='text-decoration:none;'>${name}</a>
</span> - v${junctionangle_version}
</div>
<div class="aja-option-container">
<input type=checkbox class='aja-checkbox' id='aja-scriptenabled' />
<label class='aja-label' for='aja-enableScript'><span id='aja-text-enableScript'>${TRANSLATIONS.default.scriptenabled}</span></label>
</div>
<div class='aja-option-container'>
<input type=checkbox class='aja-checkbox' id='aja-check' />
<label class='aja-label' for='aja-CeckTIOs'><span id='aja-text-Check'>${TRANSLATIONS.default.check}</span></label>
</div>
<div class='aja-option-container'>
<input type=checkbox class='aja-checkbox' id='aja-skiproundabout' />
<label class='aja-label' for='aja-SkipRoundabout'><span id='aja-text-SkipRoundabout'>${TRANSLATIONS.default.skiproundabout}</span></label>
</div>
</div>`
].join(' '));
// Attach HTML for tab to webpage
// UpdateObj = require('Waze/Action/UpdateObject');
// Script is initialized and the highlighting layer is created
WazeWrap.Interface.Tab('BJAI', $section.html(), initializeSettings, 'BJAI');
WazeWrap.Interface.ShowScriptUpdate(name, junctionangle_version, AJA_UPDATE_NOTES, 'https://greasyfork.org/en/scripts/434562-wme-bad-junction-angle-info', 'https://www.waze.com/forum/viewtopic.php?t=334486');
async function initializeSettings() {
await loadSettings();
setEleStatus();
$(`<style type="text/css">${css}</style>`).appendTo('head');
}
//Add support for translations. Default (and fallback) is "en".
//Note, don't make typos in "acceleratorName", as it has to match the layer name (with whitespace removed)
// to actually work. Took me a while to figure that out...
I18n.translations[window.I18n.locale].layers.name.alljunction_angles = name;
/**
* Initialize BJAI OpenLayers vector layer
*/
if (window.W.map.getLayersBy("uniqueName","alljunction_angles").length === 0) {
// Create a vector layer and give it your style map.
aja_mapLayer = new window.OpenLayers.Layer.Vector(name, {
displayInLayerSwitcher: true,
uniqueName: "alljunction_angles",
shortcutKey: "S+j",
accelerator: "toggle" + name.replace(/\s+/g,''),
className: "alljunction-angles",
styleMap: new window.OpenLayers.StyleMap(aja_style())
});
window.W.map.addLayer(aja_mapLayer);
aja_log("version " + junctionangle_version + " loaded.", 0);
aja_log(window.W.map, 3);
aja_log(window.W.model, 3);
aja_log(window.W.loginManager, 3);
aja_log(window.W.selectionManager, 3);
aja_log(aja_mapLayer, 3);
aja_log(window.OpenLayers, 3);
} else {
aja_log("Oh, nice.. We already had a layer?", 3);
}
WazeWrap.Interface.AddLayerCheckbox("display", "Bad Junction Angle Info", true, LayerToggled);
$('.aja-checkbox').change(function () {
let settingName = $(this)[0].id.substr(4);
AJASettings[settingName] = this.checked;
saveSettings();
aja_mapLayer.setVisibility(AJASettings.scriptenabled);
aja_calculate();
});
aja_apply();
// MTE mode event
// reload after changing WME units
W.prefs.on('change:isImperial', function(){
aja_apply();
});
aja_calculate();
}
function LayerToggled(checked){
aja_mapLayer.setVisibility(checked);
AJASettings.scriptenabled = checked;
setChecked('aja-scriptenabled', AJASettings.scriptenabled);
}
function findLayer(partOf_id){
var layer;
for (var i=0; i < window.W.map.layers.length; i++){
if (window.W.map.layers[i].id.search(partOf_id) != -1){
layer={id: window.W.map.layers[i].id, name: window.W.map.layers[i].name, number : i};
aja_log("Number: " + i + "; id : " + layer.id + "; name :" + layer.name ,3);
return layer;
}
}
}
function testLayerZIndex(){
// seb-d59:
// Here i search the selection layer and i read the z-index
// and put BJAI's layer under this one.
var zIndex = 0;
aja_mapLayer.setZIndex(500);
// now selection layer has no name ...
var layer = {}
layer = findLayer("OpenLayers_Layer_Vector_RootContainer");
var layerOBJ = window.W.map.layers[layer.number];
//aja_log("id : " + layerOBJ.id + "; name :" + layerOBJ.name + " zIndex: " + layerOBJ.getZIndex() ,3);
zIndex = parseInt(layerOBJ.getZIndex()) - 1 ;
aja_mapLayer.setZIndex(zIndex);
aja_log("aja_mapLayer new zIndex: " + aja_mapLayer.getZIndex() ,3);
}
function aja_calculate_real() {
var aja_start_time = Date.now();
var aja_nodes = [];
var restart = false;
aja_log("Actually calculating now", 2);
aja_roundabout_points = [];
aja_log(window.W.map, 3);
if (typeof aja_mapLayer === 'undefined') {
return;
}
//clear old info
aja_mapLayer.destroyFeatures();
testLayerZIndex();
if (!AJASettings.scriptenabled) return;
_.each(W.model.segments.getObjectArray(), s => {
if(![5, 10, 16, 18, 19].includes(s.attributes.roadType)){
let segmentsAtt = s.attributes;
if (segmentsAtt.fromNodeID != null &&
aja_nodes.indexOf(segmentsAtt.fromNodeID) === -1) {
aja_nodes.push(segmentsAtt.fromNodeID);
}
if (segmentsAtt.toNodeID != null &&
aja_nodes.indexOf(segmentsAtt.toNodeID) === -1) {
aja_nodes.push(segmentsAtt.toNodeID);
}
}
});
aja_log(aja_nodes, 3);
var aja_label_distance;
/*
* Define a base distance to markers, depending on the zoom level
*/
switch (window.W.map.olMap.zoom) {
case 22: //10:
aja_label_distance = 2.8;
break;
case 21: //9:
aja_label_distance = 4;
break;
case 20: //8:
aja_label_distance = 8;
break;
case 19: //7:
aja_label_distance = 15;
break;
case 18: //6:
aja_label_distance = 25;
break;
case 17: //5:
aja_label_distance = 40;
break;
case 16: //4:
aja_label_distance = 80;
break;
case 15: //3:
aja_label_distance = 150;
break;
case 14: //2:
aja_label_distance = 300;
break;
case 13: //1:
aja_label_distance = 400;
break;
default:
aja_log("Unsupported zoom level: " + window.W.map.olMap.zoom + "!", 2);
}
aja_label_distance *= (1 + (0.2 * parseInt(decimals)));
aja_log("zoom: " + window.W.map.olMap.zoom + " -> distance: " + aja_label_distance, 2);
//Start looping through selected nodes
for (var i = 0; i < aja_nodes.length; i++) {
var node = getByID(window.W.model.nodes,aja_nodes[i]);
var angles = [];
var aja_selected_segments_count = 0;
var aja_selected_angles = [];
var a;
if (node == null || !node.hasOwnProperty('attributes')) {
//Oh oh.. should not happen? We want to use a node that does not exist
aja_log("Oh oh.. should not happen?",2);
aja_log(node, 2);
aja_log(aja_nodes[i], 2);
aja_log(window.W.model, 3);
aja_log(window.W.model.nodes, 3);
continue;
}
//check connected segments
var aja_current_node_segments = node.attributes.segIDs;
aja_log("Alle aja_current_node_segments",2);
aja_log(aja_current_node_segments.length,2);
aja_log(node, 2);
aja_log(aja_current_node_segments.length,2);
//ignore of we have less than 2 segments
if (aja_current_node_segments.length <= 2) {
aja_log("Found only " + aja_current_node_segments.length + " connected segments at " + aja_nodes[i] +
", not calculating anything...", 2);
continue;
}
aja_log("Mehr als 2 aja_current_node_segments",2);
aja_log(aja_current_node_segments.length,2);
aja_log("Calculating angles for " + aja_current_node_segments.length + " segments", 2);
aja_log(aja_current_node_segments, 3);
aja_current_node_segments.forEach(function (nodeSegment, j) {
var s = window.W.model.segments.objects[nodeSegment];
if(typeof s === 'undefined') {
//Meh. Something went wrong, and we lost track of the segment. This needs a proper fix, but for now
// it should be sufficient to just restart the calculation
aja_log("Failed to read segment data from model. Restarting calculations.", 1);
if(aja_last_restart === 0) {
aja_last_restart = new Date().getTime();
setTimeout(function(){aja_calculate();}, 500);
}
restart = true;
}
a = aja_getAngle(aja_nodes[i], s);
aja_log("Segment " + nodeSegment + " angle is " + a, 2);
angles[j] = [a, nodeSegment, s == null ? false : true, s.attributes.roadType];
if (s == null ? false : true) {
aja_selected_segments_count++;
}
});
if(restart) { return; }
for( var ii = 0; ii < angles.length; ii++){
if([5, 10, 16, 18, 19].includes(angles[ii][3])){
angles.splice(ii, 1);
ii--;
}
}
aja_log(angles, 2);
var ha, point;
//sort angle data (ascending)
angles.sort(function (a, b) {
return a[0] - b[0];
});
aja_log(angles, 3);
aja_log(aja_selected_segments_count, 3);
//get all segment angles
for (var iii = 0; iii < angles.length - 1; iii++) {
for (var jjj = iii + 1; jjj < angles.length; jjj++) {
a = (360 + (angles[(jjj) % angles.length][0] - angles[iii][0])) % 360;
if (a > 180) {
a = 360 - a;
ha = (360 + ((a / 2) + angles[jjj][0])) % 360;
} else {
ha = (360 + ((a / 2) + angles[iii][0])) % 360;
}
aja_log(a,3);
aja_log("Angle between " + angles[iii][1] + " and " + angles[(jjj) % angles.length][1] + " is " +
a + " and position for label should be at " + ha, 1);
// if (a < 10.26 || (a > 133 && a < 136 )) {
if ((a > 133 && a < 136 )) {
point = new window.OpenLayers.Geometry.Point(
node.getOLGeometry().x + (aja_label_distance * 1.25 * Math.cos((ha * Math.PI) / 180)),
node.getOLGeometry().y + (aja_label_distance * 1.25 * Math.sin((ha * Math.PI) / 180))
);
// Respect Trunrestrictons and TIOs
var s1 = getByID(window.W.model.segments,angles[iii][1])
var s2 = getByID(window.W.model.segments,angles[(jjj) % angles.length][1])
var draw_marker = true;
if (AJASettings.check) {
draw_marker = false;
if (aja_is_turn_allowed(s1, node, s2)) {
var WazeModelGraphTurnData = window.require("Waze/Model/Graph/TurnData");
var turn = new WazeModelGraphTurnData();
turn = window.W.model.getTurnGraph().getTurnThroughNode(node, s1, s2);
var opcode = turn.getTurnData().getInstructionOpcode();
if(!opcode) {
if (aja_is_turn_allowed(s2, node, s1)) {
turn = window.W.model.getTurnGraph().getTurnThroughNode(node, s2, s1);
opcode = turn.getTurnData().getInstructionOpcode();
if(!opcode) {
draw_marker = true;
}
}
}
}
if (s1.attributes.junctionID || s2.attributes.junctionID) {
draw_marker = false;
}
}
if (AJASettings.skiproundabout) {
if(getByID(window.W.model.segments,angles[iii][1]).attributes.junctionID) {
draw_marker = false;
}
if(getByID(window.W.model.segments,angles[(jjj) % angles.length][1]).attributes.junctionID) {
draw_marker = false;
}
}
if (draw_marker) {
aja_draw_marker(point, node, aja_label_distance, a, ha);
}
}
}
}
}
aja_last_restart = 0;
var aja_end_time = Date.now();
aja_log("Calculation took " + String(aja_end_time - aja_start_time) + " ms", 2);
}
/*
* Drawing functions
*/
/**
*
* @param point Estimated point for marker
* @param node Node the marker is for
* @param aja_label_distance Arbitrary distance to be used in moving markers further away etc
* @param a Angle to display
* @param ha Angle to marker from node (FIXME: either point or ha is probably unnecessary)
* @param withRouting true: show routing guessing markers, false: show "normal" angle markers
* @param aja_junction_type If using routing, this needs to be set to the desired type
*/
function aja_draw_marker(point, node, aja_label_distance, a, ha, withRouting, aja_junction_type) {
//Try to estimate of the point is "too close" to another point
//(or maybe something else in the future; like turn restriction arrows or something)
//FZ69617: Exctract initial label distance from point
var aja_tmp_distance = Math.abs(ha) % 180 < 45 || Math.abs(ha) % 180 > 135 ?
(point.x - node.getOLGeometry().x) / (Math.cos((ha * Math.PI) / 180)) :
(point.y - node.getOLGeometry().y) / (Math.sin((ha * Math.PI) / 180));
aja_log("Starting distance estimation", 3);
while(aja_mapLayer.features.some(function(feature){
if(typeof feature.attributes.aja_type !== 'undefined' && feature.attributes.aja_type !== 'roundaboutOverlay') {
//Arbitrarily chosen minimum distance.. Should actually use the real bounds of the markers,
//but that didn't work out.. Bounds are always 0..
if(aja_label_distance / 1.4 > feature.geometry.distanceTo(point)) {
aja_log(aja_label_distance / 1.5 > feature.geometry.distanceTo(point) + " is kinda close..", 3);
return true;
}
}
return false;
})) {
//add 1/4 of the original distance and hope for the best =)
aja_tmp_distance += aja_label_distance / 4;
aja_log("setting distance to " + aja_tmp_distance, 2);
point = new window.OpenLayers.Geometry.Point(
node.getOLGeometry().x + (aja_tmp_distance * Math.cos((ha * Math.PI) / 180)),
node.getOLGeometry().y + (aja_tmp_distance * Math.sin((ha * Math.PI) / 180))
);
}
aja_log("Distance estimation done", 3);
var anglePoint = new window.OpenLayers.Feature.Vector(
point,
{ angle: aja_round(180 - a) + "°", aja_type: "generic" }
);
aja_log(anglePoint, 3);
//Draw a line to the point
aja_mapLayer.addFeatures([
new window.OpenLayers.Feature.Vector(
new window.OpenLayers.Geometry.LineString([node.getOLGeometry(), point]),
{},
{strokeOpacity: 0.9, strokeWidth: 2.2, strokeDashstyle: "solid", strokeColor: "#ff9966"}
)
]
);
//push the angle point
aja_mapLayer.addFeatures([anglePoint]);
}
function aja_get_first_point(segment) {
return segment.getOLGeometry().components[0];
}
function aja_get_last_point(segment) {
return segment.getOLGeometry().components[segment.getOLGeometry().components.length - 1];
}
function aja_get_second_point(segment) {
return segment.getOLGeometry().components[1];
}
function aja_get_next_to_last_point(segment) {
return segment.getOLGeometry().components[segment.getOLGeometry().components.length - 2];
}
//get the absolute angle for a segment end point
function aja_getAngle(aja_node, aja_segment) {
aja_log("node: " + aja_node, 2);
aja_log("segment: " + aja_segment, 2);
if (aja_node == null || aja_segment == null) { return null; }
var aja_dx, aja_dy;
if (aja_segment.attributes.fromNodeID === aja_node) {
aja_dx = aja_get_second_point(aja_segment).x - aja_get_first_point(aja_segment).x;
aja_dy = aja_get_second_point(aja_segment).y - aja_get_first_point(aja_segment).y;
} else {
aja_dx = aja_get_next_to_last_point(aja_segment).x - aja_get_last_point(aja_segment).x;
aja_dy = aja_get_next_to_last_point(aja_segment).y - aja_get_last_point(aja_segment).y;
}
aja_log(aja_node + " / " + aja_segment + ": dx:" + aja_dx + ", dy:" + aja_dy, 2);
var aja_angle = Math.atan2(aja_dy, aja_dx);
return ((aja_angle * 180 / Math.PI)) % 360;
}
/**
* Decimal adjustment of a number. Borrowed (with some modifications) from
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
* aja_round(55.55); with 1 decimal // 55.6
* aja_round(55.549); with 1 decimal // 55.5
* aja_round(55); with -1 decimals // 60
* aja_round(54.9); with -1 decimals // 50
*
* @param {Number} value The number.
* @returns {Number} The adjusted value.
*/
function aja_round(value) {
var aja_rounding = -parseInt(decimals);
var valueArray;
if (typeof aja_rounding === 'undefined' || +aja_rounding === 0) {
return Math.round(value);
}
value = +value;
// If the value is not a number or the exp is not an integer...
if (isNaN(value) || !(typeof aja_rounding === 'number' && aja_rounding % 1 === 0)) {
return NaN;
}
// Shift
valueArray = value.toString().split('e');
value = Math.round(+(valueArray[0] + 'e' + (valueArray[1] ? (+valueArray[1] - aja_rounding) : -aja_rounding)));
// Shift back
valueArray = value.toString().split('e');
return +(valueArray[0] + 'e' + (valueArray[1] ? (+valueArray[1] + aja_rounding) : aja_rounding));
}
/*
* WME interface helper functions
*/
var aja_apply = function applyAJAOptions() {
aja_log("Applying stored (or default) settings", 2);
if(typeof window.W.map.getLayersBy("uniqueName","alljunction_angles")[0] === 'undefined') {
aja_log("WME not ready yet, trying again in 400 ms", 2);
setTimeout(function(){aja_apply();}, 400);
return;
}
window.W.map.getLayersBy("uniqueName","alljunction_angles")[0].styleMap = aja_style();
aja_calculate_real();
aja_log(aja_options, 2);
};
var aja_reset = function resetAJAOptions() {
aja_log("Resetting settings", 2);
if(localStorage != null) {
localStorage.removeItem("wme_bja_options");
}
aja_options = {};
aja_apply();
return false;
};
var aja_calculation_timer = {
start: function() {
aja_log("Starting timer", 2);
this.cancel();
var aja_calculation_timer_self = this;
this.timeoutID = window.setTimeout(function(){aja_calculation_timer_self.calculate();}, 200);
},
calculate: function() {
aja_calculate_real();
delete this.timeoutID;
},
cancel: function() {
if(typeof this.timeoutID === "number") {
window.clearTimeout(this.timeoutID);
aja_log("Cleared timeout ID : " + this.timeoutID, 2);
delete this.timeoutID;
}
}
};
function aja_calculate() {
aja_calculation_timer.start();
}
function aja_style() {
aja_log("Point radius will be: " + (parseInt(pointSize, 10)) +
(parseInt(decimals > 0 ? (4 * parseInt(decimals)).toString() : "0")), 2);
return new window.OpenLayers.Style({
fillColor: "#ffff00",
strokeColor: "#ff9966",
strokeWidth: 2,
label: "${angle}",
fontWeight: "bold",
pointRadius: parseInt(pointSize, 10) +
(parseInt(decimals) > 0 ? 4 * parseInt(decimals) : 0),
fontSize: "10px"
});
}
function aja_is_turn_allowed(s_from, via_node, s_to) {
aja_log("Allow from " + s_from.attributes.id +
" to " + s_to.attributes.id +
" via " + via_node.attributes.id + "? " +
via_node.isTurnAllowedBySegDirections(s_from, s_to) + " | " + s_from.isTurnAllowed(s_to, via_node), 2);
//Is there a driving direction restriction?
if(!via_node.isTurnAllowedBySegDirections(s_from, s_to)) {
aja_log("Driving direction restriction applies", 3);
return false;
}
//Is turn allowed by other means (e.g. turn restrictions)?
if(!s_from.isTurnAllowed(s_to, via_node)) {
aja_log("Other restriction applies", 3);
return false;
}
if(s_to.attributes.fromNodeID === via_node.attributes.id) {
aja_log("FWD direction",3);
return aja_is_car_allowed_by_restrictions(s_to.attributes.fwdRestrictions);
} else {
aja_log("REV direction",3);
return aja_is_car_allowed_by_restrictions(s_to.attributes.revRestrictions);
}
}
function aja_is_car_allowed_by_restrictions(restrictions) {
aja_log("Checking restrictions for cars", 2);
if(typeof restrictions === 'undefined' || restrictions == null || restrictions.length === 0) {
aja_log("No car type restrictions to check...", 3);
return true;
}
aja_log(restrictions, 3);
return !restrictions.some(function(element) {
/*jshint bitwise: false*/
aja_log("Checking restriction " + element, 3);
//noinspection JSBitwiseOperatorUsage
var ret = element.allDay && //All day restriction
element.days === 127 && //Every week day
( element.vehicleTypes === -1 || //All vehicle types
element.vehicleTypes & aja_vehicle_types.PRIVATE //or at least private cars
);
if (ret) {
aja_log("There is an all-day-all-week restriction", 3);
var fromDate = Date.parse(element.fromDate);
var toDate = Date.parse(element.toDate);
aja_log("From: " + fromDate + ", to: " + toDate + ". " + ret, 3);
if(isNaN(fromDate && isNaN(toDate))) {
aja_log("No start nor end date defined");
return false;
}
var fRes, tRes;
if(!isNaN(fromDate) && new Date() > fromDate) {
aja_log("From date is in the past", 3);
fRes = 2;
} else if(isNaN(fromDate)) {
aja_log("From date is invalid/not set", 3);
fRes = 1;
} else {
aja_log("From date is in the future: " + fromDate, 3);
fRes = 0;
}
if(!isNaN(toDate) && new Date() < toDate) {
aja_log("To date is in the future", 3);
tRes = 2;
} else if(isNaN(toDate)) {
aja_log("To date is invalid/not set", 3);
tRes = 1;
} else {
aja_log("To date is in the past: " + toDate, 3);
tRes = 0;
}
// Car allowed unless
// - toDate is in the future and fromDate is unset or in the past
// - fromDate is in the past and toDate is unset in the future
// Hope I got this right ;)
return (fRes <= 1 && tRes <= 1);
}
return ret;
});
}
async function loadSettings() {
const localSettings = $.parseJSON(localStorage.getItem('AJA_Settings'));
console.log('AJA Settings loaded');
// Attempt connection to WazeWrap setting server to retrieve settings
const serverSettings = await WazeWrap.Remote.RetrieveSettings('AJA_Settings');
if (!serverSettings) console.log('AJA: Error communicating with WW settings server');
// Default checkbox settings
const defaultsettings = {
lastSaveAction: 0,
scriptenabled: true,
check: false,
skiproundabout: false
};
AJASettings = $.extend({}, defaultsettings, localSettings);
if (serverSettings && serverSettings.lastSaveAction > AJASettings.lastSaveAction) {
$.extend(AJASettings, serverSettings);
// console.log('AJA: server settings used');
} else {
// console.log('AJA: local settings used');
}
// If there is no value set in any of the stored settings then use the default
Object.keys(defaultsettings).forEach((funcProp) => {
if (!AJASettings.hasOwnProperty(funcProp)) {
AJASettings[funcProp] = defaultsettings[funcProp];
}
});
}
async function saveSettings() {
const {
lastSaveAction,
scriptenabled,
check,
skiproundabout
} = AJASettings;
const localSettings = {
lastSaveAction: Date.now(),
scriptenabled,
check,
skiproundabout
};
// Required for the instant update of changes to the keyboard shortcuts on the UI
AJASettings = localSettings;
if (localStorage) {
localStorage.setItem('AJA_Settings', JSON.stringify(localSettings));
}
const serverSave = await WazeWrap.Remote.SaveSettings('AJA_Settings', localSettings);
if (serverSave === null) {
console.warn('AJA: User PIN not set in WazeWrap tab');
} else {
if (serverSave === false) {
console.error('AJA: Unable to save settings to server');
}
}
if (serverSave === true) {
console.log('AJA: Settings saved');
}
}
// Set user options
function setEleStatus() {
setChecked('aja-scriptenabled', AJASettings.scriptenabled);
setChecked('aja-skiproundabout', AJASettings.skiproundabout);
if ( country == 'Austria' ) {
$(`#${'aja-check'}`).prop('disabled', true);
} else {
setChecked('aja-check', AJASettings.check);
}
}
function setChecked(ele, status) {
$(`#${ele}`).prop('checked', status);
}
/*
* Bootstrapping and logging
*/
function aja_bootstrap(retries) {
retries = retries || 0;
//If Waze has not been defined in ~15 seconds, it probably won't work anyway. Might need tuning
//for really slow devices?
if (retries >= 30) {
aja_log("Failed to bootstrap 30 times. Giving up.", 0);
return;
}
try {
//User logged in and WME ready
if (
// !(document.querySelector('.list-unstyled.togglers .group') === null) &&
aja_is_model_ready() &&
aja_is_dom_ready() &&
checkCountry() &&
window.W.loginManager.isLoggedIn()) {
setTimeout(function () {
junctionangle_init();
}, 500);
}
//Some part of the WME was not yet fully loaded. Retry.
else {
setTimeout(function () {
aja_bootstrap(++retries);
}, 500);
}
} catch (err) {
aja_log(err, 1);
setTimeout(function () {
aja_bootstrap(++retries);
}, 500);
}
}
function aja_is_model_ready() {
if(typeof window.W === 'undefined' || typeof window.W.map === 'undefined') {
return false;
} else {
//return 'undefined' !== typeof window.W.map.events.register &&
return 'undefined' !== typeof window.W.map.olMap.events.register &&
'undefined' !== typeof window.W.selectionManager.events.register &&
'undefined' !== typeof window.W.loginManager.events.register;
}
}
function aja_is_dom_ready() {
if(null === document.getElementById('user-info')) {
return false;
} else {
return document.getElementById('user-info').getElementsByClassName('nav-tabs').length > 0 &&
document.getElementById('user-info').getElementsByClassName('tab-content').length > 0;
}
}
/**
* Debug logging.
* @param aja_log_msg
* @param aja_log_level
*/
function aja_log(aja_log_msg, aja_log_level) {
if(typeof aja_log_level === 'undefined') { aja_log_level = 1; }
if (aja_log_level <= junctionangle_debug) {
if (typeof aja_log_msg === "object") {
console.log(aja_log_msg);
}
else {
console.log("WME Bad JAI: " + aja_log_msg);
}
}
}
function getByID(obj, id){
if (typeof(obj.getObjectById) == "function"){
return obj.getObjectById(id);
}else if (typeof(obj.getObjectById) == "undefined"){
return obj.get(id);
}
}
function checkCountry() {
try {
country = W.model.getTopCountry().attributes.name;
} catch (err) {
country = null;
// console.log(err);
}
return country;
}
aja_bootstrap();
}
let run_aja_script = GM_addElement('script', {
textContent: "" + run_aja.toString() + " \n" + "run_aja();"
});