WME RTC Manager

Utility to manage RTC

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name           WME RTC Manager
// @description    Utility to manage RTC
// @namespace      [email protected]
// @grant          none
// @grant          GM_info
// @version        0.0.2
// @include 	     /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @exclude        https://www.waze.com/user/*editor/*
// @exclude        https://www.waze.com/*/user/*editor/*
// @author         GyllieGyllie
// @license        MIT/BSD/X11
// @require        https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// ==/UserScript==
/* Changelog

*/
/* global W */
/* global I18n */
/* global $ */

const ScriptName = GM_info.script.name;
const ScriptVersion = GM_info.script.version;

let ChangeLog = "RTC Manager has been updated to " + ScriptVersion + "<br />";
ChangeLog = ChangeLog + "<br /><b>New: </b>";
ChangeLog = ChangeLog + "<br />" + "- (0.0.1) Ability to select all linked segments with a matching closure";
ChangeLog = ChangeLog + "<br /><b>Updated: </b>";
ChangeLog = ChangeLog + "<br />" + "- (0.0.2) Fix not all segments being selected in some scenarios";

let wmeSDK;
const options = loadOptions();

// Now validate the options are ok
validateOptions(options);

function log(message) {
  if (typeof message === 'string') {
    console.log('RTC Manager: ' + message);
  } else {
    console.log('RTC Manager: ', message);
  }
}

// the sdk init function will be available after the WME is initialized
function WMERTCManager_bootstrap() {
  if (!document.getElementById('edit-panel') || !wmeSDK.DataModel.Countries.getTopCountry() || !WazeWrap.Ready) {
    setTimeout(WMERTCManager_bootstrap, 250);
    return;
  }

  if (wmeSDK.State.isReady) {
    WMERTCManager_init();
  } else {
    wmeSDK.Events.once({ eventName: "wme-ready" }).then(WMERTCManager_init);
  }
}

function WMERTCManager_init() {
  log("Start");

  // check for changes in the edit-panel
  const speedLimitsObserver = new MutationObserver((mutations) => {
    mutations.forEach(function(mutation) {
      // Mutation is a NodeList and doesn't support forEach like an array
      for (let i = 0; i < mutation.addedNodes.length; i++) {
        const addedNode = mutation.addedNodes[i];

        // Only fire up if it's a node
        if (addedNode.nodeType === Node.ELEMENT_NODE) {
          if (addedNode.querySelector('div.closures')) {
            makeListButtons();
          }
        }
      }

      makeDetailsButtons();
    });
  });

  speedLimitsObserver.observe(document.getElementById('edit-panel'), { childList: true, subtree: true });

  // Catch permalinks
  //makeSigns();

  constructSettings();
  displayChangelog();

  log("Done");

}

// Check if unsafeWindow is availabe, if so use that
('unsafeWindow' in window ? window.unsafeWindow : window).SDK_INITIALIZED.then(() => {
  // initialize the sdk with your script id and script name
  wmeSDK = getWmeSdk({scriptId: "wme-rtc-manager", scriptName: "RTC Manager"});
  WMERTCManager_bootstrap();
});

function displayChangelog() {
  if (!WazeWrap.Interface) {
    setTimeout(displayChangelog, 1000);
    return;
  }

  // Alert the user version updates
  if (options.lastAnnouncedVersion === ScriptVersion) {
    log('Version: ' + ScriptVersion);
  } else {
    WazeWrap.Interface.ShowScriptUpdate(ScriptName, ScriptVersion, ChangeLog + "<br /><br />", "https://github.com/wazers/rtc-manager");

    const updateName = "#wmertcmanager" + ScriptVersion.replaceAll(".", "");
    $(updateName + " .WWSUFooter a").text("Github")

    options.lastAnnouncedVersion = ScriptVersion;
    saveOptions(options);
  }
}

function makeListButtons() {

  const container = $('div.closures-list');

  /*const selectAllButton = $('<wz-button size="sm" style="width: 100%; margin-top: 10px;">Select all</wz-button>');
  selectAllButton.on('click', selectAll);
  container.append(selectAllButton);*/

  /*const saveButton = $('<wz-button size="sm" style="width: 100%; margin-top: 10px;">Delete all</wz-button>');
  saveButton.on('click', deleteAll);
  container.append(saveButton);*/

}

function makeDetailsButtons() {

  const container = $('div.closure');

  if (!container) {
    return;
  }

  const footer = $('div.closure div.action-buttons');

  if ($("#rtcm-detail-all").length > 0) {
    return;
  }

  const selectAllButton = $('<wz-button id="rtcm-detail-all" size="sm"">Select all</wz-button>');
  selectAllButton.on('click', selectAll);
  footer.append(selectAllButton);
}

function selectAll() {

  const ids = [];
  const footerIds = $('div[class^="closureFooterFragmentContainer"] > div > li');
  footerIds.each((el) => ids.push($(footerIds[el]).text()));

  if (ids.length === 0) return;

  const nearbyClosures = wmeSDK.DataModel.RoadClosures.getAll();

  let closure = nearbyClosures.find(closure => ids.indexOf(closure.id) >= 0);

  const segmentIds = [];
  const scanned = new Set();
  if (closure) {
    const segment = wmeSDK.DataModel.Segments.getById({ segmentId: closure.segmentId });

    // Segment not found
    if (!segment) {
      return;
    }

    appendSegments(scanned, segmentIds, segment, closure);
  }

  wmeSDK.Editing.setSelection({
    selection: {
      ids: segmentIds,
      objectType: 'segment'
    }
  });
}

function deleteAll() {

  const selection = wmeSDK.Editing.getSelection();

  if ("segment" !== selection.objectType) {
    return;
  }

  const nearbyClosures = wmeSDK.DataModel.RoadClosures.getAll();

  console.log(selection);
  for (let segmentId of selection.ids) {
    console.log(segmentId);

    const segment = wmeSDK.DataModel.Segments.getById({ segmentId: segmentId });
    console.log(segment);

    if (!segment.hasClosures) {
      continue;
    }
  }


}

function appendSegments(scanned, segmentIds, segment, originalClosure) {

  // This was already scanned
  if (scanned.has(segment.id)) {
    return;
  }

  scanned.add(segment.id);
  const closures = wmeSDK.DataModel.RoadClosures.getAll().filter(rc => rc.segmentId === segment.id);

  // See if any closure matches
  for (let closure of closures) {
    if (originalClosure.description === closure.description
      && originalClosure.startDate === closure.startDate
      && originalClosure.endDate === closure.endDate
      && originalClosure.trafficEventId === closure.trafficEventId) {

      segmentIds.push(segment.id);
      break;
    }
  }

  let linkedSegments = wmeSDK.DataModel.Segments.getConnectedSegments({ segmentId: segment.id });

  for (let ls of linkedSegments) {
    appendSegments(scanned, segmentIds, ls, originalClosure);
  }

  linkedSegments = wmeSDK.DataModel.Segments.getConnectedSegments({ segmentId: segment.id, reverseDirection: true });

  for (let ls of linkedSegments) {
    appendSegments(scanned, segmentIds, ls, originalClosure);
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////
////
//// Option Logic
////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
function constructSettings() {

  // -- Set up the tab for the script
  wmeSDK.Sidebar.registerScriptTab().then(({ tabLabel, tabPane }) => {
    tabLabel.innerText = 'RTC Manager';
    tabLabel.title = 'RTC Manager Settings';

    tabPane.innerHTML = '<div id="rtc-manager-settings"></div>';

    const scriptContentPane = $('#rtc-manager-settings');

    scriptContentPane.append(`<h2 style="margin-top: 0;">RTC Manager</h2>`);
    scriptContentPane.append(`<span>Current Version: <b>${ScriptVersion}</b></span>`);

    //addTextNumberSettings(scriptContentPane, '', 'Icon Scale in %', 'iconScale');
    //addBooleanSettingsCallback(scriptContentPane, '', 'Enable Clear Sign', 'clearSign', toggleBoolean);
  });

}

function getDefaultOptions() {
  return {
    lastAnnouncedVersion: '',
  }
}

function loadOptions() {
  let text = localStorage.getItem("RTC-Manager-Options");
  let options;

  if (text) {
    options = JSON.parse(text);
  } else {
    options = getDefaultOptions();
  }

  return options;
}

function validateOptions(options) {
  const defaultOptions = getDefaultOptions();

  // Add missing options
  for (let key in defaultOptions) {
    if (!(key in options)) {
      options[key] = defaultOptions[key]
    }
  }
}

function saveOptions(options) {
  const optionsJson = JSON.stringify(options);
  localStorage.setItem("RTC-Manager-Options", optionsJson);
}

function changeText(event) {
  options[event.target.id] = event.target.value;
  saveOptions(options);
}

function addTextNumberSettings(container, title, label, name, step = 1) {
  const currentValue = options[name];

  const textInput = $('<wz-text-input type="number" min="0" max="999" step="' + step + '" id="' + name + '" value="' + currentValue + '"></wz-text-input>');
  const optionHtml = $('<div style="margin-top: 10px;"><span Title="' + title + '">' + label + '</span></div>').append(textInput);

  container.append(optionHtml);

  textInput.on('change', changeText);
}

function addBooleanSettingsCallback(container, title, label, name, clickHandler) {
  const currentValue = options[name];

  const checkbox = $('<wz-checkbox id="' + name + '" Title="' + title + '" name="types" disabled="false" checked="' + currentValue + '">' + label + '</wz-checkbox>');
  const optionHtml = $('<div class="urcom-option"></div>').append(checkbox);

  container.append(optionHtml);

  checkbox.on('click', clickHandler);
}

function toggleBoolean(event) {
  options[event.target.id] = event.target.checked;
  saveOptions(options);
}