WME Wazebar

Displays a bar at the top of the editor that displays inbox, forum & wiki links

目前为 2025-04-07 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name WME Wazebar
  3. // @namespace https://greasyfork.org/users/30701-justins83-waze
  4. // @version 2025.04.06.01
  5. // @description Displays a bar at the top of the editor that displays inbox, forum & wiki links
  6. // @author JustinS83
  7. // @include https://beta.waze.com/*
  8. // @include https://www.waze.com/discuss/*
  9. // @include https://webnew.waze.com/discuss/*
  10. // @include https://www.waze.com/editor*
  11. // @include https://www.waze.com/*/editor*
  12. // @exclude https://www.waze.com/user/editor*
  13. // @require https://greasyfork.org/scripts/27254-clipboard-js/code/clipboardjs.js
  14. // @connect storage.googleapis.com
  15. // @connect greasyfork.org
  16. // @grant none
  17. // @contributionURL https://github.com/WazeDev/Thank-The-Authors
  18. // ==/UserScript==
  19.  
  20. /* global $ */
  21. /* global I18n */
  22.  
  23. (function () {
  24. "use strict";
  25. var WazeBarSettings = [];
  26. var forumInterval;
  27. var currentState = "";
  28. var States = {};
  29. var forumUnreadOffset = 0;
  30. const SCRIPT_VERSION = GM_info.script.version.toString();
  31. const SCRIPT_NAME = GM_info.script.name;
  32. const DOWNLOAD_URL = GM_info.script.downloadURL;
  33. var debug = false;
  34. let wmeSDK;
  35.  
  36. var isBeta = /beta/.test(location.href);
  37. var forumPage = /discuss/.test(location.href);
  38.  
  39. console.log(`${SCRIPT_NAME}: isBeta:`, isBeta);
  40. console.log(`${SCRIPT_NAME}: forumPage:`, forumPage);
  41.  
  42. if (!forumPage) {
  43. window.SDK_INITIALIZED.then(bootstrap);
  44. } else {
  45. initScript();
  46. }
  47.  
  48. function bootstrap() {
  49. console.log(`${SCRIPT_NAME}: bootstrap() called`);
  50. wmeSDK = getWmeSdk({
  51. scriptId: SCRIPT_NAME.replaceAll(" ", ""),
  52. scriptName: SCRIPT_NAME,
  53. });
  54.  
  55. Promise.all([wmeReady()])
  56. .then(() => {
  57. console.log(`${SCRIPT_NAME}: All dependencies are ready.`);
  58. initScript();
  59. })
  60. .catch((error) => {
  61. console.error(`${SCRIPT_NAME}: Error during bootstrap -`, error);
  62. });
  63. }
  64.  
  65. function wmeReady() {
  66. return new Promise((resolve) => {
  67. if (wmeSDK.State.isReady()) {
  68. resolve();
  69. } else {
  70. wmeSDK.Events.once({ eventName: "wme-ready" }).then(resolve);
  71. }
  72. });
  73. }
  74.  
  75. function initScript() {
  76. if (debug) console.log(`${SCRIPT_NAME}: initScript() called`);
  77. if (forumPage) {
  78. loadScript("https://use.fontawesome.com/73f886e1d5.js", loadAppComponents);
  79. } else {
  80. loadAppComponents();
  81. }
  82. }
  83.  
  84. function loadAppComponents() {
  85. if (debug) console.log(`${SCRIPT_NAME}: loadAppComponents() called`);
  86. LoadSettingsObj();
  87. LoadStatesObj();
  88.  
  89. if (!forumPage || (forumPage && WazeBarSettings.DisplayWazeForum)) {
  90. if (!forumPage) {
  91. if (wmeSDK.Events) {
  92. // Register map move events
  93. wmeSDK.Events.on({
  94. eventName: "wme-map-move-end",
  95. eventHandler: () => setTimeout(updateCurrentStateEntries, 100),
  96. });
  97.  
  98. // Register map zoom change event
  99. wmeSDK.Events.on({
  100. eventName: "wme-map-zoom-changed",
  101. eventHandler: () => setTimeout(updateCurrentStateEntries, 100),
  102. });
  103. }
  104. }
  105. BuildWazebar();
  106. injectCss();
  107. BuildSettingsInterface();
  108. console.log(`${SCRIPT_NAME}: Waze Bar Loaded`);
  109. }
  110. }
  111.  
  112. function loadScript(url, callback) {
  113. var script = document.createElement("script");
  114. script.type = "text/javascript";
  115.  
  116. if (script.readyState) {
  117. //IE
  118. script.onreadystatechange = function () {
  119. if (script.readyState == "loaded" || script.readyState == "complete") {
  120. script.onreadystatechange = null;
  121. if (callback != null) callback();
  122. }
  123. };
  124. } else {
  125. //Others
  126. script.onload = function () {
  127. if (callback != null) callback();
  128. };
  129. }
  130.  
  131. script.src = url;
  132. document.getElementsByTagName("head")[0].appendChild(script);
  133. }
  134.  
  135. function getCurrentState() {
  136. const topState = wmeSDK.DataModel.States.getTopState();
  137. if (debug) console.log(`${SCRIPT_NAME}: getCurrentState() called: topState =`, topState);
  138. if (topState === null) {
  139. return null; // Handle the case where no top state is available
  140. }
  141. return topState.name;
  142. }
  143.  
  144. function updateCurrentStateEntries() {
  145. const topState = wmeSDK.DataModel.States.getTopState();
  146. if (debug) console.log(`${SCRIPT_NAME}: updateCurrentStateEntries() called: topState =`, topState);
  147.  
  148. if (topState !== null && currentState !== getCurrentState()) {
  149. // User panned/zoomed to a different state, so we need to update the current state forum & wiki entries
  150. BuildWazebar();
  151. currentState = getCurrentState();
  152. }
  153. }
  154.  
  155. function BuildWazebar() {
  156. if (debug) console.log(`${SCRIPT_NAME}: BuildWazebar() called`);
  157. $("#Wazebar").remove();
  158. var $Wazebar = $("<div>", { id: "Wazebar" });
  159. $Wazebar.html(
  160. [
  161. '<div class="WazeBarIcon" id="WazeBarSettingsButton"><i class="fa fa-cog" aria-hidden="true"></i></div>',
  162. '<div class="WazeBarIcon" id="WazeBarRefreshButton"><i class="fa fa-refresh" aria-hidden="true"></i></div>',
  163. '<div class="WazeBarIcon" id="WazeBarFavoritesIcon"><i class="fa fa-star" aria-hidden="true"></i>',
  164. '<div id="WazeBarFavorites">',
  165. '<ul id="WazeBarFavoritesList"></ul>',
  166. '<div id="WazeBarFavoritesAddContainer">',
  167. '<input type="text" id="WazeBarURL" placeholder="URL">',
  168. '<input type="text" id="WazeBarText" placeholder="Label">',
  169. '<button id="WazeBarAddFavorite">Add</button>',
  170. "</div>",
  171. "</div>",
  172. "</div>",
  173. // Other forum links
  174. WazeBarSettings.WMEBetaForum ? '<div class="WazeBarText WazeBarForumItem" id="WMEBetaForum"><a href="https://www.waze.com/discuss/c/editors/beta-community/4088" ' + LoadNewTab() + ">WME Beta</a></div>" : "",
  175. WazeBarSettings.scriptsForum ? '<div class="WazeBarText WazeBarForumItem" id="ScriptsForum"><a href="https://www.waze.com/discuss/c/editors/addons-extensions-and-scripts/3984" ' + LoadNewTab() + ">Scripts</a></div>" : "",
  176. WazeBarSettings.USSMForum ? '<div class="WazeBarText WazeBarForumItem" id="USSMForum"><a href="https://www.waze.com/discuss/c/editors/united-states/us-state-managers/4890" ' + LoadNewTab() + ">US SM</a></div>" : "",
  177. WazeBarSettings.USChampForum ? '<div class="WazeBarText WazeBarForumItem" id="USChampForum"><a href="https://www.waze.com/discuss/c/editors/united-states/us-waze-champs/4893" ' + LoadNewTab() + ">US Champ</a></div>" : "",
  178. WazeBarSettings.USWikiForum ? '<div class="WazeBarText WazeBarForumItem" id="USWikiForum"><a href="https://www.waze.com/discuss/c/editors/united-states/us-wiki-discussion/4894" ' + LoadNewTab() + ">US Wiki</a></div>" : "",
  179. BuildStateForumEntries(),
  180. BuildStateUnlockEntries(),
  181. BuildCustomEntries(),
  182. BuildRegionWikiEntries(),
  183. BuildStateWikiEntries(),
  184. BuildCurrentStateEntries(),
  185. WazeBarSettings.NAServerUpdate ? '<div class="WazeBarText WazeBarServerUpdate;" id="WazebarStatus">NA Server Update: </div>' : "",
  186. WazeBarSettings.ROWServerUpdate ? '<div class="WazeBarText WazeBarServerUpdate;" id="WazebarStatusROW">ROW Server Update: </div>' : "",
  187. ].join("")
  188. );
  189.  
  190. function prependWazebarToHeader(retries = 5, interval = 200) {
  191. if (forumPage) {
  192. const attemptPrepend = () => {
  193. const header = $(".d-header");
  194. if (header.length) {
  195. header.prepend($Wazebar);
  196. $("#Wazebar").css({
  197. "background-color": "white",
  198. width: "100%",
  199. });
  200. } else if (retries > 0) {
  201. retries--;
  202. setTimeout(attemptPrepend, interval);
  203. } else {
  204. console.warn("Warning: .d-header not found after multiple attempts.");
  205. }
  206. };
  207. attemptPrepend();
  208. } else {
  209. $(".app.container-fluid").before($Wazebar);
  210. }
  211. }
  212. prependWazebarToHeader();
  213.  
  214. checkForums();
  215. StartIntervals();
  216.  
  217. // Event handler for settings button to show the settings dialog
  218. $("#WazeBarSettingsButton").click(function () {
  219. if (debug) console.log(`${SCRIPT_NAME}: Settings button clicked`);
  220. $("#WazeBarSettings").fadeIn();
  221. });
  222.  
  223. $("#WazeBarAddFavorite").click(function () {
  224. if (debug) console.log(`${SCRIPT_NAME}: AddFavorite button clicked`);
  225. var url = $("#WazeBarURL").val();
  226. var text = $("#WazeBarText").val();
  227. if (url !== "" && text !== "") {
  228. if (!(url.startsWith("http://") || url.startsWith("https://"))) {
  229. url = "http://" + url;
  230. }
  231. WazeBarSettings.Favorites.push({ href: url, text: text });
  232. $("#WazeBarURL").val("");
  233. $("#WazeBarText").val("");
  234. LoadFavorites();
  235. SaveSettings();
  236. }
  237. });
  238.  
  239. $("#WazeBarFavoritesIcon").mouseleave(function () {
  240. $("#WazeBarFavorites").css({ display: "none" });
  241. });
  242.  
  243. $("#WazeBarFavoritesIcon").mouseenter(function () {
  244. $("#WazeBarFavorites").css({ display: "block" });
  245. });
  246.  
  247. LoadFavorites();
  248.  
  249. $("#WazeBarFavoritesList a").click(function () {
  250. $("#WazeBarFavorites").css({ display: "none" });
  251. });
  252.  
  253. if (WazeBarSettings.NAServerUpdate) {
  254. fetch("https://storage.googleapis.com/waze-tile-build-public/release-history/na-feed-v2.xml")
  255. .then((response) => {
  256. if (!response.ok) {
  257. throw new Error(`${SCRIPT_NAME}: Network response was not ok: ${response.statusText}`);
  258. }
  259. return response.text();
  260. })
  261. .then((data) => ParseStatusFeed(data, "NA", "WazebarStatus"))
  262. .catch((error) => console.error(`${SCRIPT_NAME}: Error fetching NA Server Update:`, error));
  263. }
  264.  
  265. if (WazeBarSettings.ROWServerUpdate) {
  266. fetch("https://storage.googleapis.com/waze-tile-build-public/release-history/intl-feed-v2.xml")
  267. .then((response) => {
  268. if (!response.ok) {
  269. throw new Error(`${SCRIPT_NAME}: Network response was not ok: ${response.statusText}`);
  270. }
  271. return response.text();
  272. })
  273. .then((data) => ParseStatusFeed(data, "ROW", "WazebarStatusROW"))
  274. .catch((error) => console.error(`${SCRIPT_NAME}: Error fetching ROW Server Update:`, error));
  275. }
  276.  
  277. $("#WazeBarRefreshButton").click(function () {
  278. if (debug) console.log(`${SCRIPT_NAME}: Refresh button clicked`);
  279. $("#WazeBarRefreshButton i").addClass("fa-spin");
  280. window.clearInterval(forumInterval);
  281. checkForums();
  282. StartIntervals();
  283. $("#WazeBarRefreshButton i").removeClass("fa-spin");
  284. });
  285.  
  286. // Initially set height for the app container
  287. setHeightForAppContainer();
  288. }
  289.  
  290. // Function for setting height dynamically
  291. function setHeightForAppContainer() {
  292. if (debug) console.log(`${SCRIPT_NAME}: setHeightForAppContainer called`);
  293. const wazebarHeight = $("#Wazebar").height();
  294. $("body > div.app.container-fluid").css("height", `calc(100vh - ${wazebarHeight}px)`);
  295. window.dispatchEvent(new Event("resize")); // Adjust WME editing area
  296. }
  297.  
  298. function LoadSettingsInterface() {
  299. if (debug) console.log(`${SCRIPT_NAME}: LoadSettingsInterface() called`);
  300. // Load JSON settings and use default if not present
  301. const loadedSettings = localStorage.getItem("Wazebar_Settings") || JSON.stringify(defaultSettings, null, 4);
  302. $("#txtWazebarSettings")[0].innerHTML = loadedSettings;
  303.  
  304. // Update the UI elements
  305. setChecked("WazeForumSetting", WazeBarSettings?.DisplayWazeForum !== undefined ? WazeBarSettings.DisplayWazeForum : defaultSettings.DisplayWazeForum);
  306. setChecked("WMEBetaForumSetting", WazeBarSettings?.WMEBetaForum !== undefined ? WazeBarSettings.WMEBetaForum : defaultSettings.WMEBetaForum);
  307. setChecked("ScriptsForum", WazeBarSettings?.scriptsForum !== undefined ? WazeBarSettings.scriptsForum : defaultSettings.scriptsForum);
  308. setChecked("USSMForumSetting", WazeBarSettings?.USSMForum !== undefined ? WazeBarSettings.USSMForum : defaultSettings.USSMForum);
  309. if (!forumPage) setChecked("USChampForumSetting", WazeBarSettings?.USChampForum !== undefined ? WazeBarSettings.USChampForum : defaultSettings.USChampForum);
  310. setChecked("USWikiForumSetting", WazeBarSettings?.USWikiForum !== undefined ? WazeBarSettings.USWikiForum : defaultSettings.USWikiForum);
  311. setChecked("NAServerUpdateSetting", WazeBarSettings?.NAServerUpdate !== undefined ? WazeBarSettings.NAServerUpdate : defaultSettings.NAServerUpdate);
  312. setChecked("ROWServerUpdateSetting", WazeBarSettings?.ROWServerUpdate !== undefined ? WazeBarSettings.ROWServerUpdate : defaultSettings.ROWServerUpdate);
  313. $("#forumInterval")[0].value = WazeBarSettings?.forumInterval !== undefined ? WazeBarSettings.forumInterval : defaultSettings.forumInterval;
  314. $("#forumHistory")[0].value = WazeBarSettings?.forumHistory !== undefined ? WazeBarSettings.forumHistory : defaultSettings.forumHistory;
  315. $("#WazeBarFontSize")[0].value = WazeBarSettings?.BarFontSize !== undefined ? WazeBarSettings.BarFontSize : defaultSettings.BarFontSize;
  316. $("#colorPickerForumFont").val(WazeBarSettings?.ForumFontColor !== undefined ? WazeBarSettings.ForumFontColor : defaultSettings.ForumFontColor);
  317. $("#colorPickerWikiFont").val(WazeBarSettings?.WikiFontColor !== undefined ? WazeBarSettings.WikiFontColor : defaultSettings.WikiFontColor);
  318. serializeSettings();
  319. LoadCustomLinks();
  320. }
  321.  
  322. function LoadNewTab() {
  323. return forumPage ? "" : ' target="_blank"';
  324. }
  325.  
  326. function LoadFavorites() {
  327. if (debug) console.log(`${SCRIPT_NAME}: LoadFavorites() called`);
  328. var favoritesList = $("#WazeBarFavoritesList");
  329. favoritesList.empty(); // Clear the list
  330.  
  331. // For each favorite, append a structured item
  332. WazeBarSettings.Favorites.forEach((favorite, index) => {
  333. const listItem = $(`
  334. <li class="WazeBarFavoritesList favorite-item">
  335. <a href="${favorite.href}" target="_blank">${favorite.text}</a>
  336. <i class="fa fa-times" title="Remove from favorites" data-index="${index}"></i>
  337. </li>
  338. `);
  339. favoritesList.append(listItem);
  340. });
  341.  
  342. // Use event delegation to handle the removal of items
  343. favoritesList.on("click", ".fa-times", function () {
  344. const index = $(this).data("index");
  345. WazeBarSettings.Favorites.splice(index, 1);
  346. SaveSettings();
  347. LoadFavorites();
  348. });
  349. }
  350.  
  351. function LoadCustomLinks() {
  352. if (debug) console.log(`${SCRIPT_NAME}: LoadCustomLinks() called`);
  353. const customList = $("#WazeBarCustomLinksList");
  354. customList.empty(); // Clear the list
  355. // Append structured items for each custom link
  356. WazeBarSettings.CustomLinks.forEach((customLink, index) => {
  357. const listItem = $(`
  358. <li class="custom-item">
  359. <a href="${customLink.href}"${LoadNewTab()}>${customLink.text}</a>
  360. <i class="fa fa-times" title="Remove custom link" data-index="${index}"></i>
  361. </li>
  362. `);
  363. customList.append(listItem);
  364. });
  365. // Handle removal using event delegation
  366. customList.off("click", ".fa-times").on("click", ".fa-times", function () {
  367. const index = $(this).data("index");
  368. if (index >= 0 && index < WazeBarSettings.CustomLinks.length) {
  369. WazeBarSettings.CustomLinks.splice(index, 1);
  370. serializeSettings();
  371. LoadCustomLinks();
  372. BuildWazebar();
  373. }
  374. });
  375. // Ensure index management when using close functionality
  376. $('[id^="WazeBarCustomLinksListClose"]').off("click").on("click", function () {
  377. const index = Number(this.id.replace("WazeBarCustomLinksListClose", ""));
  378. if (index >= 0 && index < WazeBarSettings.CustomLinks.length) {
  379. WazeBarSettings.CustomLinks.splice(index, 1);
  380. serializeSettings();
  381. LoadCustomLinks();
  382. BuildWazebar();
  383. }
  384. });
  385. }
  386.  
  387. function StartIntervals() {
  388. forumInterval = setInterval(checkForums, WazeBarSettings.forumInterval * 60000);
  389. }
  390.  
  391. function checkForums() {
  392. if (debug) console.log(`${SCRIPT_NAME}: checkForums() called`);
  393. if (WazeBarSettings.WMEBetaForum) checkUnreadTopics("https://www.waze.com/discuss/c/editors/beta-community/4088", "WMEBetaForum", "WMEBetaForumCount");
  394. if (WazeBarSettings.scriptsForum) checkUnreadTopics("https://www.waze.com/discuss/c/editors/addons-extensions-and-scripts/3984", "ScriptsForum", "ScriptsCount");
  395. if (WazeBarSettings.USSMForum) checkUnreadTopics("https://www.waze.com/discuss/c/editors/united-states/us-state-managers/4890", "USSMForum", "USSMForumCount");
  396. if (WazeBarSettings.USChampForum) checkUnreadTopics("https://www.waze.com/discuss/c/editors/united-states/us-waze-champs/4893", "USChampForum", "USChampForumCount");
  397. if (WazeBarSettings.USWikiForum) checkUnreadTopics("https://www.waze.com/discuss/c/editors/united-states/us-wiki-discussion/4894", "USWikiForum", "USWikiForumCount");
  398.  
  399. Object.keys(WazeBarSettings.header).forEach(function (state, index) {
  400. if (WazeBarSettings.header[state].forum) checkUnreadTopics(WazeBarSettings.header[state].forum, state.replace(" ", "_") + "Forum", state.replace(" ", "_") + "ForumCount");
  401.  
  402. if (WazeBarSettings.header[state].unlock) {
  403. var url = "https://www.waze.com/discuss/search?q=" + encodeURIComponent(state) + "%20%23united-states%3Aus-unlock-and-update-requests%20order%3Alatest";
  404. checkUnreadTopics(url, state.replace(" ", "_") + "Unlock", state.replace(" ", "_") + "UnlockCount");
  405. }
  406. });
  407.  
  408. for (var i = 0; i < WazeBarSettings.CustomLinks.length; i++) {
  409. if (WazeBarSettings.CustomLinks[i].href.includes("/discuss/")) checkUnreadTopics(WazeBarSettings.CustomLinks[i].href, WazeBarSettings.CustomLinks[i].text.replace(/\s/g, "") + i + "Forum", WazeBarSettings.CustomLinks[i].text.replace(/\s/g, "") + i + "ForumCount"); // JS55CT TEST
  410. }
  411. }
  412.  
  413. // General Function logic for checkUnreadTopics() from dalverson Github fork - Oct 10, 20204
  414. function checkUnreadTopics(path, parentID, spanID) {
  415. var count = 0;
  416. var jdat, dat1;
  417.  
  418. if (debug) console.log(`${SCRIPT_NAME}: CheckUnreadTopics() called for `, path, parentID, spanID);
  419.  
  420. $.get(path, function (page) {
  421. const jpattern = /data-preloaded=\"(.*)\">/;
  422. var dat = jpattern.exec(page);
  423.  
  424. if (dat && dat.length > 1) {
  425. dat1 = dat[1].replace(/&quot;/g, '"');
  426. try {
  427. jdat = JSON.parse(dat1);
  428. } catch (error) {
  429. console.error(`${SCRIPT_NAME}: JSON parse error in checkUnreadTopics()`, error);
  430. return;
  431. }
  432.  
  433. var jdat2;
  434. var topix;
  435.  
  436. if (jdat.search) {
  437. jdat2 = JSON.parse(jdat.search);
  438. topix = jdat2.topics; // Access topics directly from search JSON format
  439. } else if (jdat.topic_list) {
  440. jdat2 = JSON.parse(jdat.topic_list);
  441. topix = jdat2.topic_list?.topics; // Access topics from the nested topic_list property
  442. } else {
  443. console.warn(`${SCRIPT_NAME}: invalid JSON format in checkUnreadTopics() for `, parentID);
  444. return;
  445. }
  446.  
  447. if (!topix || topix.length === 0) {
  448. console.warn(`${SCRIPT_NAME}: No topics found in checkUnreadTopics() for`, parentID);
  449. return;
  450. }
  451.  
  452. $("#" + spanID).remove();
  453. var links = "";
  454. for (var tp in topix) {
  455. if (Object.prototype.hasOwnProperty.call(topix, tp)) {
  456. var tobj = topix[tp];
  457. const ldate = Date.parse(tobj.last_posted_at);
  458. const formattedDate = formatDate(new Date(ldate));
  459. const diff = Date.now() - ldate;
  460. const dfhrs = diff / 3600000; // hours since last post on this topic
  461. var lrpn = tobj.last_read_post_number ? tobj.last_read_post_number : 0;
  462. var hpn = tobj.highest_post_number ? tobj.highest_post_number : 0;
  463. var item_to_read = lrpn > 0 && lrpn < hpn ? lrpn + 1 : hpn;
  464. var fh = WazeBarSettings.forumHistory * 24;
  465.  
  466. if (((lrpn > 0 && lrpn < hpn) || (dfhrs < fh && lrpn == 0) || tobj.unseen || tobj.unread > 0 || tobj.unread_posts > 0) && dfhrs < fh) {
  467. count += 1;
  468. links += `
  469. <li class="WazeBarUnreadList unread-item">
  470. <a href="https://www.waze.com/discuss/t/${tobj.slug}/${tobj.id}/${item_to_read}" ${LoadNewTab()}>${tobj.fancy_title} (${formattedDate})</a>
  471. </li>`;
  472. }
  473. }
  474. }
  475.  
  476. if (count > 0) {
  477. $("#" + parentID + " a").append(`
  478. <span style='color:red;font-weight:bold;' id='${spanID}'>
  479. (${count})
  480. <div class='WazeBarUnread' id='WazeBarUnread${spanID}' style='visibility:hidden;
  481. animation-fill-mode: forwards;
  482. left:${$("#" + parentID).position().left}px;
  483. top:${parseInt($("#" + parentID).height()) + forumUnreadOffset}px;'>
  484. <ul class='WazeBarUnreadList' id='WazeBarUnreadList${spanID}'>
  485. </ul>
  486. </div>
  487. </span>
  488. `);
  489.  
  490. $("#WazeBarUnreadList" + spanID).html(links);
  491.  
  492. $("#" + spanID)
  493. .on("mouseenter", function () {
  494. $("#WazeBarUnread" + spanID).css({ visibility: "visible" });
  495. })
  496. .on("mouseleave", function () {
  497. $("#WazeBarUnread" + spanID).css({ visibility: "hidden" });
  498. });
  499.  
  500. $("#" + spanID + " a").click(function (event) {
  501. event.stopPropagation();
  502. $("#WazeBarUnread" + spanID).css({ visibility: "hidden" });
  503. });
  504. }
  505. } else {
  506. console.warn(`${SCRIPT_NAME}: No <data-preloaded> section on /discuss webpage for `, parentID);
  507. }
  508. });
  509. return count;
  510. }
  511.  
  512. function ParseStatusFeed(data, updateType, targetId) {
  513. if (debug) console.log(`${SCRIPT_NAME}: ParseStatusFeed() called with updateType = ${updateType}`);
  514.  
  515. // Parse the XML data
  516. const parser = new DOMParser();
  517. const xmlDoc = parser.parseFromString(data, "application/xml");
  518.  
  519. if (xmlDoc.querySelector("parsererror")) {
  520. console.error(`${SCRIPT_NAME}: Error parsing XML`);
  521. return;
  522. }
  523.  
  524. // Get the first <entry> element
  525. const firstEntry = xmlDoc.querySelector("entry");
  526. if (!firstEntry) {
  527. console.error(`${SCRIPT_NAME}: No entry element found`);
  528. return;
  529. }
  530.  
  531. // Extract and check the <title> element
  532. const title = firstEntry.querySelector("title")?.textContent || "";
  533. if ((updateType === "NA" && title.includes("North America")) || (updateType === "ROW" && title.includes("International"))) {
  534. // Extract the <updated> element
  535. const updated = firstEntry.querySelector("updated")?.textContent;
  536. if (debug) console.log(`${SCRIPT_NAME}: Parsed <update> object: ${updated}`);
  537.  
  538. if (!updated) {
  539. console.error(`${SCRIPT_NAME}: No updated element found`);
  540. return;
  541. }
  542.  
  543. const date = new Date(updated); // The date string is expected to already be in UTC (Z) format
  544. if (isNaN(date)) {
  545. console.error(`${SCRIPT_NAME}: Unable to convert <update> date: ${updated}`);
  546. } else {
  547. if (debug) console.log(`${SCRIPT_NAME}: Converted <update> to date object: ${date}`);
  548.  
  549. const formattedDate = formatDate(date);
  550. const label = updateType === "NA" ? "NA Server Update: " : "ROW Server Update: ";
  551. const $target = $("#" + targetId);
  552. $target.empty().append(label + formattedDate);
  553.  
  554. if (debug) console.log(`${SCRIPT_NAME}: Update found for ${updateType}: ${formattedDate}`);
  555. }
  556. } else {
  557. if (debug) console.log(`${SCRIPT_NAME}: Title does not match expected format for ${updateType}`);
  558. }
  559. }
  560.  
  561. function formatDate(date) {
  562. // Extract the date in YYYY-MM-DD format
  563. const dateOptions = { year: "numeric", month: "2-digit", day: "2-digit" };
  564. const dateString = date.toLocaleDateString("en-CA", dateOptions);
  565.  
  566. // Extract the time in HH:MM AM/PM format
  567. const timeOptions = { hour: "2-digit", minute: "2-digit", hour12: true };
  568. const timeString = date.toLocaleTimeString("en-US", timeOptions);
  569.  
  570. // Combine and format the date and time
  571. return `${dateString} ${timeString}`;
  572. }
  573.  
  574. function BuildStateForumEntries() {
  575. var stateForums = "";
  576. Object.keys(WazeBarSettings.header).forEach(function (state) {
  577. if (WazeBarSettings.header[state].forum)
  578. stateForums += '<div class="WazeBarText WazeBarForumItem" id="' + state.replace(" ", "_") + 'Forum"><a href="' + WazeBarSettings.header[state].forum + '" ' + LoadNewTab() + ">" + WazeBarSettings.header[state].abbr + "</a></div>";
  579. });
  580. return stateForums;
  581. }
  582.  
  583. function BuildCurrentStateEntries() {
  584. var currentState = "";
  585.  
  586. if (!forumPage) {
  587. const topCountry = wmeSDK.DataModel.Countries.getTopCountry();
  588. const topCountryId = topCountry ? topCountry.id : null;
  589.  
  590. if (topCountryId === 235) {
  591. // Only proceed if the top country is the US
  592. var currState = getCurrentState();
  593. if (!currState || !States[currState]) {
  594. return currentState; // Return an empty string if currState or its corresponding States entry is invalid.
  595. }
  596. currentState += '<div class="WazeBarText WazeBarCurrState" id="' + currState.replace(" ", "_") + 'ForumCurrState"><a href="' + States[currState].forum + '" ' + LoadNewTab() + ">" + States[currState].abbr + "</a></div>";
  597. currentState += '<div class="WazeBarText WazeBarCurrState"><a href="' + States[currState].wiki + '"' + LoadNewTab() + ">" + States[currState].abbr + " Wiki</a></div>";
  598. }
  599. }
  600. return currentState;
  601. }
  602.  
  603. function BuildCustomEntries() {
  604. var customList = "";
  605. if (WazeBarSettings.CustomLinks && WazeBarSettings.CustomLinks.length > 0) {
  606. //Categories like Forum Entries & User Profiles
  607. for (var i = 0; i < WazeBarSettings.CustomLinks.length; i++) {
  608. if (WazeBarSettings.CustomLinks[i].href.includes("/discuss/c/") || WazeBarSettings.CustomLinks[i].href.includes("/discuss/u/")) {
  609. customList +=
  610. '<div class="WazeBarText WazeBarForumItem" id="' +
  611. WazeBarSettings.CustomLinks[i].text.replace(/\s/g, "") +
  612. i +
  613. 'Forum"><a href="' +
  614. WazeBarSettings.CustomLinks[i].href +
  615. '" ' +
  616. LoadNewTab() +
  617. ">" +
  618. WazeBarSettings.CustomLinks[i].text +
  619. "</a></div>";
  620. }
  621. }
  622.  
  623. for (i = 0; i < WazeBarSettings.CustomLinks.length; i++) {
  624. if (
  625. WazeBarSettings.CustomLinks[i].href.includes("/discuss/t/") ||
  626. WazeBarSettings.CustomLinks[i].href.includes("/discuss/tag/")
  627. ) {
  628. customList +=
  629. '<div class="WazeBarText WazeBarWikiItem" id="' +
  630. WazeBarSettings.CustomLinks[i].text.replace(/\s/g, "") +
  631. i +
  632. 'Forum"><a href="' + //'Wiki"><a href="' +
  633. WazeBarSettings.CustomLinks[i].href +
  634. '" ' +
  635. LoadNewTab() +
  636. ">" +
  637. WazeBarSettings.CustomLinks[i].text +
  638. "</a></div>";
  639. }
  640. }
  641.  
  642. return customList;
  643. }
  644. }
  645.  
  646. function BuildStateWikiEntries() {
  647. var stateWikis = "";
  648. Object.keys(WazeBarSettings.header).forEach(function (state) {
  649. if (WazeBarSettings.header[state].wiki) {
  650. stateWikis +=
  651. '<div class="WazeBarText WazeBarWikiItem"><a href="' +
  652. WazeBarSettings.header[state].wiki +
  653. '"' +
  654. LoadNewTab() +
  655. ">" +
  656. WazeBarSettings.header[state].abbr +
  657. " Wiki</a></div>";
  658. }
  659. });
  660. return stateWikis;
  661. }
  662.  
  663. function BuildStateUnlockEntries() {
  664. var stateUnlocks = "";
  665. Object.keys(WazeBarSettings.header).forEach(function (state) {
  666. if (WazeBarSettings.header[state].unlock) {
  667. // Construct the URL with the correct use of encodeURIComponent
  668. var url = `https://www.waze.com/discuss/search?q=${encodeURIComponent(state)}%20%23united-states%3Aus-unlock-and-update-requests%20order%3Alatest`;
  669. stateUnlocks += '<div class="WazeBarText WazeBarForumItem" id="' + state.replace(" ", "_") + 'Unlock"><a href="' + url + '" ' + LoadNewTab() + ">" + WazeBarSettings.header[state].abbr + " Unlock</a></div>";
  670. }
  671. });
  672. return stateUnlocks;
  673. }
  674.  
  675. function BuildRegionWikiEntries() {
  676. var regionWikis = "";
  677. if (WazeBarSettings.header.region) {
  678. Object.keys(WazeBarSettings.header.region).forEach(function (region) {
  679. if (WazeBarSettings.header.region[region].wiki) {
  680. regionWikis +=
  681. '<div class="WazeBarText WazeBarWikiItem"><a href="' +
  682. WazeBarSettings.header.region[region].wiki +
  683. '"' +
  684. LoadNewTab() +
  685. ">" +
  686. WazeBarSettings.header.region[region].abbr +
  687. " Wiki</a></div>";
  688. }
  689. });
  690. }
  691. return regionWikis;
  692. }
  693.  
  694. function BuildSettingsInterface() {
  695. if (debug) console.log(`${SCRIPT_NAME}: BuildSettingsInterface() called`);
  696.  
  697. var $section = $("<div>", { id: "WazeBarSettings" });
  698. $section.html(
  699. [
  700. "<div>",
  701. "<div class='flex-container' style='margin-bottom: 10px;'>",
  702.  
  703. // Start of the 1st Flex Column
  704. "<div class='flex-column left-column'>",
  705. "<div style='display: flex; flex-direction: column; gap: 4px;'>",
  706. // Font size with default value
  707. "<div style='display: flex; align-items: center; gap: 4px;'>",
  708. "<input type='number' id='WazeBarFontSize' min='8' max='30' style='width: 50px; height: 20px' value='" + WazeBarSettings.BarFontSize + "'/>",
  709. "<label for='WazeBarFontSize'>Font size</label>",
  710. "</div>",
  711. // Forum font color with default value
  712. "<div style='display: flex; align-items: center; gap: 4px;'>",
  713. "<input type='color' id='colorPickerForumFont' style='width: 50px; height: 25px' value='" + WazeBarSettings.ForumFontColor + "'/>",
  714. "<label for='colorPickerForumFont'>Forum link Color</label>",
  715. "</div>",
  716. // Wiki font color with default value
  717. "<div style='display: flex; align-items: center; gap: 4px;'>",
  718. "<input type='color' id='colorPickerWikiFont' style='width: 50px; height: 25px' value='" + WazeBarSettings.WikiFontColor + "'/>",
  719. "<label for='colorPickerWikiFont'>Wiki link Color</label>",
  720. "</div>",
  721.  
  722. // Forum check frequency
  723. "<div style='display: flex; align-items: center; gap: 4px;'>",
  724. "<input type='number' id='forumInterval' min='1' style='width: 50px; height: 20px' value='" + WazeBarSettings.forumInterval + "'/>",
  725. "<label for='forumInterval'>Refresh freq. (mins)</label>",
  726. "</div>",
  727. // Forum History
  728. "<div style='display: flex; align-items: center; gap: 4px;'>",
  729. "<input type='number' id='forumHistory' min='1' style='width: 50px; height: 20px' value='" + WazeBarSettings.forumHistory + "'/>",
  730. "<label for='forumHistory'>History (Days)</label>",
  731. "</div>",
  732. // Horizontal rule before Custom Links section
  733. "<hr>",
  734.  
  735. // Export/Import Section
  736. "<div id='exportImportSection'>",
  737. "<h4>Export/Import</h4>",
  738. "<div class='flex-row' style='align-items: flex-start; gap: 4px;'>",
  739. "<button class='export-button fa fa-upload' id='btnWazebarCopySettings' title='Copy Wazebar settings to the clipboard' data-clipboard-target='#txtWazebarSettings'></button>",
  740. "<textarea readonly id='txtWazebarSettings' placeholder='Copied settings will appear here'></textarea>",
  741. "</div>",
  742. "<div class='flex-row' style='align-items: flex-start; gap: 4px; margin-top: 8px;'>",
  743. "<button class='import-button fa fa-download' id='btnWazebarImportSettings' title='Import copied settings'></button>",
  744. "<textarea id='txtWazebarImportSettings' placeholder='Paste JSON formated settings here to import'></textarea>",
  745. "</div>",
  746. "</div>",
  747. "</div>",
  748. "</div>",
  749.  
  750. // Start of the 2nd Flex Column
  751. // Start of the major Forums and Server update check boxes
  752. "<div class='flex-column right-column'>",
  753. "<div id='WBDisplayOptions'>",
  754.  
  755. "<div class='checkbox-container'>",
  756. "<input type='checkbox' id='WazeForumSetting' " + (WazeBarSettings.DisplayWazeForum ? "checked" : "") + " />",
  757. "<label for='WazeForumSetting'>Display on Forum pages</label>",
  758. "</div>",
  759.  
  760. "<div class='checkbox-container'>",
  761. "<input type='checkbox' id='WMEBetaForumSetting' " + (WazeBarSettings.WMEBetaForum ? "checked" : "") + " />",
  762. "<label for='WMEBetaForumSetting'>WME Beta Forum</label>",
  763. "</div>",
  764.  
  765. "<div class='checkbox-container'>",
  766. "<input type='checkbox' id='ScriptsForum' " + (WazeBarSettings.scriptsForum ? "checked" : "") + " />",
  767. "<label for='ScriptsForum'>Scripts Forum</label>",
  768. "</div>",
  769.  
  770. "<div class='checkbox-container'>",
  771. "<input type='checkbox' id='USSMForumSetting' " + (WazeBarSettings.USSMForum ? "checked" : "") + " />",
  772. "<label for='USSMForumSetting'>US SM Forum</label>",
  773. "</div>",
  774.  
  775. // Conditionally render US Champ Forum checkbox
  776.  
  777. !forumPage && wmeSDK.State.getUserInfo().rank >= 5 ? ["<div class='checkbox-container'>", "<input type='checkbox' id='USChampForumSetting' " + (WazeBarSettings.USChampForum ? "checked" : "") + " />", "<label for='USChampForumSetting'>US Champ Forum</label>", "</div>"].join("") : "",
  778.  
  779. "<div class='checkbox-container'>",
  780. "<input type='checkbox' id='USWikiForumSetting' " + (WazeBarSettings.USWikiForum ? "checked" : "") + " />",
  781. "<label for='USWikiForumSetting'>US Wiki Forum</label>",
  782. "</div>",
  783.  
  784. "<div class='checkbox-container'>",
  785. "<input type='checkbox' id='NAServerUpdateSetting' " + (WazeBarSettings.NAServerUpdate ? "checked" : "") + " />",
  786. "<label for='NAServerUpdateSetting'>NA Server Update</label>",
  787. "</div>",
  788.  
  789. "<div class='checkbox-container'>",
  790. "<input type='checkbox' id='ROWServerUpdateSetting' " + (WazeBarSettings.ROWServerUpdate ? "checked" : "") + " />",
  791. "<label for='ROWServerUpdateSetting'>ROW Server Update</label>",
  792. "</div>",
  793.  
  794. // Start of the Region Dropdown and State check boxes
  795. BuildRegionDropdown(),
  796. "<div id='WBStates' style='margin-top: 12px;'></div>",
  797. "</div>",
  798. "</div>",
  799.  
  800. // Start of the 3nd Flex Column
  801. "<div class='flex-column right-column'>",
  802. // Custom Links Section
  803. "<div id='customLinksSection'>",
  804. "<h4>Custom /Discuss Links</h4>",
  805. "<ul id='WazeBarCustomLinksList'></ul>",
  806. "<div>",
  807. "<div style='display: flex; flex-direction: column;'>",
  808. "<input type='text' id='WazeBarCustomURL' placeholder='Enter URL'/>",
  809. "<input type='text' id='WazeBarCustomText' placeholder='Enter Link Text'/>",
  810. "<button id='WazeBarAddCustomLink'>Add</button>",
  811. "</div>",
  812. "</div>",
  813. "</div>",
  814. "</div>",
  815. "</div>",
  816.  
  817. // Bottom Div section with WazeBar Forum Link, Save and Cancel buttons
  818. "<div style='display: flex; justify-content: space-between; margin-top: 8px;'>",
  819. "<a href='https://www.waze.com/discuss/t/script-wazebar/208863' target='_blank'>Waze Bar Forum Thread</a>",
  820. "<span>Version: " + SCRIPT_VERSION + "</span>",
  821. "<div>",
  822. "<button id='WBSettingsSave'>Save</button>",
  823. "<button id='WBSettingsCancel'>Cancel</button>",
  824. "</div>",
  825. "</div>",
  826. ].join(" ")
  827. );
  828.  
  829. if (forumPage) {
  830. $("body").append($section);
  831. } else {
  832. $("#WazeMap").append($section);
  833. }
  834.  
  835. // Apply draggable functionality to the #WazeBarSettings div
  836. makeDialogMovable(document.getElementById('WazeBarSettings'));
  837. LoadCustomLinks();
  838. serializeSettings(); // Load the current JSON settings into the Export Text Box
  839.  
  840. $("#WazeBarAddCustomLink").click(function () {
  841. if (debug) console.log(`${SCRIPT_NAME}: Add Custom Link clicked`);
  842. if ($("#WazeBarCustomText").val() !== "" && $("#WazeBarCustomURL").val() !== "") {
  843. var url = $("#WazeBarCustomURL").val();
  844. if (!(url.startsWith("http://") || url.startsWith("https://"))) {
  845. url = "http://" + url;
  846. }
  847. WazeBarSettings.CustomLinks.push({
  848. href: url,
  849. text: $("#WazeBarCustomText").val(),
  850. });
  851. $("#WazeBarCustomURL").val("");
  852. $("#WazeBarCustomText").val("");
  853. LoadCustomLinks();
  854. SaveSettings();
  855. BuildWazebar();
  856. }
  857. });
  858.  
  859. // Cancel button logic
  860. $("#WBSettingsCancel").click(function () {
  861. if (debug) console.log(`${SCRIPT_NAME}: Settings Interface Cancel button clicked`);
  862.  
  863. LoadSettingsObj();
  864. LoadSettingsInterface();
  865. var regionValue = $("#WBRegions").val();
  866. // Check if #WBRegions has a selected value and call SelectedRegionChanged() accordingly
  867. if (regionValue !== "" && regionValue !== null) {
  868. SelectedRegionChanged();
  869. }
  870. BuildWazebar();
  871. injectCss();
  872. $("#WazeBarSettings").fadeOut();
  873. });
  874.  
  875. // Save button logic
  876. $("#WBSettingsSave").click(function () {
  877. if (debug) console.log(`${SCRIPT_NAME}: Settings Interface Save button clicked`);
  878. updateWazeBarSettingsFromUI(); // Step 1: Update settings
  879. serializeSettings(); // Step 2 & 3: Serialize settings calls Save Settings
  880. BuildWazebar(); // Step 4: Rebuild the WazeBar
  881. injectCss(); // Step 5: Inject CSS
  882. $("#WazeBarSettings").fadeOut(); // Step 6: Hide settings dialog
  883. $(".WazeBarText").css("font-size", $("#WazeBarFontSize").val() + "px"); // Step 7: Update font size
  884. setHeightForAppContainer(); // Step 8: Reside the the .app.container for any possable changes in font size of the Wazrbar
  885. });
  886.  
  887. $("#WBRegions").change(SelectedRegionChanged);
  888.  
  889. // Import Settings button logic
  890. $("#btnWazebarImportSettings").click(function () {
  891. if (debug) console.log(`${SCRIPT_NAME}: Import Settings button clicked`);
  892. const inputSettings = $("#txtWazebarImportSettings").val().trim();
  893.  
  894. if (inputSettings === "") {
  895. localAlertInfo(SCRIPT_NAME, "Import Settings Input string is empty.");
  896. return;
  897. }
  898.  
  899. if (!isValidJson(inputSettings)) {
  900. localAlertInfo(SCRIPT_NAME, "Import Settings has Invalid JSON format.");
  901. return;
  902. }
  903.  
  904. try {
  905. const parsedSettings = JSON.parse(inputSettings);
  906. if (debug) console.log(`${SCRIPT_NAME}: parsedSettings:`, parsedSettings);
  907.  
  908. if (typeof parsedSettings === "object" && parsedSettings !== null) {
  909. WazeBarSettings = { ...defaultSettings, ...parsedSettings };
  910.  
  911. // Update the UI elements to reflect imported settings
  912. LoadSettingsInterface();
  913. SelectedRegionChanged();
  914. BuildWazebar();
  915. injectCss();
  916.  
  917. localAlertInfo(SCRIPT_NAME, "Settings imported successfully and Saved. Please review.");
  918. } else {
  919. localAlertInfo(SCRIPT_NAME, "Valid JSON, but the format is not suitable for WazeBar settings.");
  920. }
  921. } catch (e) {
  922. console.error(`${SCRIPT_NAME}: Exception Details:`, e.message);
  923. console.error(e.stack);
  924. }
  925. });
  926.  
  927. function isValidJson(str) {
  928. try {
  929. JSON.parse(str);
  930. } catch (e) {
  931. console.error(`${SCRIPT_NAME}: Invalid JSON detected in #btnWazebarImportSettings: `, e.message);
  932. return false;
  933. }
  934. return true;
  935. }
  936.  
  937. // Copy Settings button logic
  938. let clipboardInstance;
  939. $("#btnWazebarCopySettings").click(function () {
  940. if (debug) console.log(`${SCRIPT_NAME}: Export/Copy Settings button clicked`);
  941. updateWazeBarSettingsFromUI(); // Step 1: Update settings
  942. serializeSettings(); // Step 2 & 3: Serialize settings calls Savesettings
  943. BuildWazebar(); // Step 4: Rebuild the WazeBar
  944. injectCss(); // Step 5: Inject CSS
  945. // Step 6: Instantiate Clipboard
  946. if (clipboardInstance) {
  947. clipboardInstance.destroy();
  948. }
  949. clipboardInstance = new Clipboard("#btnWazebarCopySettings");
  950.  
  951. if (debug) console.log(`${SCRIPT_NAME}: Clipboard = `, clipboardInstance);
  952. //Inform the user that settings are copied
  953. localAlertInfo(SCRIPT_NAME, "Your settings have been copied to the clipboard.");
  954. });
  955.  
  956. $("#WazeBarSettings").hide(); // Ensure the settings dialog is initially hidden
  957. }
  958. function makeDialogMovable(element) {
  959. var pos = { top: 0, left: 0, x: 0, y: 0 };
  960. const mouseDownHandler = function (e) {
  961. // Set the cursor
  962. element.style.cursor = 'grabbing';
  963. // Starting position
  964. pos = {
  965. left: element.offsetLeft,
  966. top: element.offsetTop,
  967. x: e.clientX,
  968. y: e.clientY,
  969. };
  970. document.addEventListener('mousemove', mouseMoveHandler);
  971. document.addEventListener('mouseup', mouseUpHandler);
  972. };
  973. const mouseMoveHandler = function (e) {
  974. const dx = e.clientX - pos.x;
  975. const dy = e.clientY - pos.y;
  976. // Update the element's position
  977. element.style.left = `${pos.left + dx}px`;
  978. element.style.top = `${pos.top + dy}px`;
  979. };
  980. const mouseUpHandler = function () {
  981. // Remove cursors and event listeners
  982. element.style.cursor = 'move';
  983. document.removeEventListener('mousemove', mouseMoveHandler);
  984. document.removeEventListener('mouseup', mouseUpHandler);
  985. };
  986. // Apply the mouse down handler to start the drag
  987. element.addEventListener('mousedown', mouseDownHandler);
  988. }
  989.  
  990. function SelectedRegionChanged() {
  991. setChecked("RegionWikiSetting", false);
  992. var selectedItem = $("#WBRegions")[0].options[$("#WBRegions")[0].selectedIndex];
  993. var region = selectedItem.value;
  994. var wiki = selectedItem.getAttribute("data-wiki");
  995.  
  996. if (!WazeBarSettings.header.region) WazeBarSettings.header.region = {};
  997. if (WazeBarSettings.header.region[region] == null) WazeBarSettings.header.region[region] = {};
  998. if (WazeBarSettings.header.region[region].wiki && WazeBarSettings.header.region[region].wiki !== "") setChecked("RegionWikiSetting", true);
  999.  
  1000. var wikiCheckboxState = $("#RegionWikiSetting").is(":checked");
  1001. BuildStatesDiv(region, wikiCheckboxState, wiki);
  1002. }
  1003.  
  1004. function BuildStatesDiv(region, wikiCheckboxState) {
  1005. // Get the state list for this region
  1006. var selectedItem = $("#WBRegions")[0].options[$("#WBRegions")[0].selectedIndex];
  1007.  
  1008. var statesData = selectedItem.getAttribute("data-states") || "";
  1009. var states = statesData.split(",").filter((state) => state.trim() !== "");
  1010.  
  1011. if (!statesData) {
  1012. if (debug) console.log(`${SCRIPT_NAME}: No data-states attribute found for selected region:`, selectedItem);
  1013. }
  1014.  
  1015. $("#WBStates").empty();
  1016.  
  1017. // Create the header row
  1018. var headerHTML = `
  1019. <div class="state-header">
  1020. <div class="state-column">Name</div>
  1021. <div class="checkbox-column">Forum</div>
  1022. <div class="checkbox-column">Wiki</div>
  1023. <div class="checkbox-column">Unlock</div>
  1024. </div>
  1025. `;
  1026.  
  1027. // Include the selected region as the first row, only with the Wiki checkbox
  1028. var regionHTML = `
  1029. <div class="state-row">
  1030. <div class="state-column">${region}</div>
  1031. <div class="checkbox-column">-</div> <!-- No Forum checkbox for region -->
  1032. <div class="checkbox-column"><input type='checkbox' id='RegionWikiSetting' ${wikiCheckboxState ? "checked" : ""} /></div>
  1033. <div class="checkbox-column">-</div> <!-- No Unlock checkbox for region -->
  1034. </div>
  1035. `;
  1036.  
  1037. // Create the state rows with all checkboxes
  1038. var statesHTML = states
  1039. .map(function (state) {
  1040. var stateId = state.replace(" ", "_");
  1041. return `
  1042. <div class="state-row">
  1043. <div class="state-column">${state}</div>
  1044. <div class="checkbox-column"><input type='checkbox' id='${stateId}ForumSetting' /></div>
  1045. <div class="checkbox-column"><input type='checkbox' id='${stateId}WikiSetting' /></div>
  1046. <div class="checkbox-column"><input type='checkbox' id='${stateId}UnlockSetting' /></div>
  1047. </div>
  1048. `;
  1049. })
  1050. .join("");
  1051.  
  1052. // Append the header and region (incl. check state) and states rows to the container
  1053. $("#WBStates").append(headerHTML + regionHTML + statesHTML);
  1054.  
  1055. $("#RegionWikiSetting").change(function () {
  1056. var selectedItem = $("#WBRegions")[0].options[$("#WBRegions")[0].selectedIndex];
  1057. var region = selectedItem.value;
  1058. var wiki = selectedItem.getAttribute("data-wiki");
  1059. var abbr = selectedItem.getAttribute("data-abbr");
  1060.  
  1061. if (!WazeBarSettings.header.region) WazeBarSettings.header.region = {};
  1062. if (WazeBarSettings.header.region[region] == null) WazeBarSettings.header.region[region] = {};
  1063. if (this.checked) {
  1064. WazeBarSettings.header.region[region].wiki = wiki;
  1065. WazeBarSettings.header.region[region].abbr = abbr;
  1066. } else {
  1067. delete WazeBarSettings.header.region[region].wiki;
  1068. }
  1069. });
  1070.  
  1071. // Checking previously saved settings (if any) and setting checkboxes accordingly
  1072. states.forEach(function (state) {
  1073. var stateKey = state.replace(" ", "_");
  1074.  
  1075. if (WazeBarSettings.header[state]) {
  1076. if (WazeBarSettings.header[state].forum && WazeBarSettings.header[state].forum !== "") {
  1077. setChecked(`${stateKey}ForumSetting`, true);
  1078. }
  1079. if (WazeBarSettings.header[state].wiki && WazeBarSettings.header[state].wiki !== "") {
  1080. setChecked(`${stateKey}WikiSetting`, true);
  1081. }
  1082. if (WazeBarSettings.header[state].unlock && WazeBarSettings.header[state].unlock !== "") {
  1083. setChecked(`${stateKey}UnlockSetting`, true);
  1084. }
  1085. }
  1086.  
  1087. $(`#${stateKey}ForumSetting`).change(function () {
  1088. var stateName = this.id.replace("ForumSetting", "").replace("_", " ");
  1089. if (!WazeBarSettings.header[stateName]) WazeBarSettings.header[stateName] = {};
  1090. if (this.checked) {
  1091. WazeBarSettings.header[stateName].forum = States[stateName].forum;
  1092. WazeBarSettings.header[stateName].abbr = States[stateName].abbr;
  1093. } else {
  1094. delete WazeBarSettings.header[stateName].forum;
  1095. }
  1096. SaveSettings();
  1097. });
  1098.  
  1099. $(`#${stateKey}WikiSetting`).change(function () {
  1100. var stateName = this.id.replace("WikiSetting", "").replace("_", " ");
  1101. if (!WazeBarSettings.header[stateName]) WazeBarSettings.header[stateName] = {};
  1102. if (this.checked) {
  1103. WazeBarSettings.header[stateName].wiki = States[stateName].wiki;
  1104. WazeBarSettings.header[stateName].abbr = States[stateName].abbr;
  1105. } else {
  1106. delete WazeBarSettings.header[stateName].wiki;
  1107. }
  1108. SaveSettings();
  1109. });
  1110.  
  1111. $(`#${stateKey}UnlockSetting`).change(function () {
  1112. var stateName = this.id.replace("UnlockSetting", "").replace("_", " ");
  1113. if (!WazeBarSettings.header[stateName]) WazeBarSettings.header[stateName] = {};
  1114. if (this.checked) {
  1115. WazeBarSettings.header[stateName].unlock = "https://www.waze.com/discuss/search?q=" + encodeURIComponent(stateName) + "%20%23united-states%3Aus-unlock-and-update-requests%20order%3Alatest";
  1116. WazeBarSettings.header[stateName].abbr = States[stateName].abbr;
  1117. } else {
  1118. delete WazeBarSettings.header[stateName].unlock;
  1119. }
  1120. SaveSettings();
  1121. });
  1122. });
  1123. }
  1124.  
  1125. function BuildRegionDropdown() {
  1126. var $places = $("<div>");
  1127. $places.html(
  1128. [
  1129. '<select id="WBRegions" class="styled-select">',
  1130. '<option value="" selected disabled>Select Region</option>', // Default placeholder option
  1131. '<option value="Northwest" data-abbr="NWR" data-states="Alaska,Idaho,Montana,Washington,Oregon,Wyoming" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-northwest/378999">Northwest</option>',
  1132. '<option value="Southwest" data-abbr="SWR" data-states="Arizona,California,Colorado,Hawaii,Nevada,New Mexico,Utah" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-southeast/379033">Southwest</option>',
  1133. '<option value="Plains" data-abbr="PLN" data-states="Iowa,Kansas,Minnesota,Missouri,Nebraska,North Dakota,South Dakota" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-plains/379009">Plains</option>',
  1134. '<option value="South Central" data-abbr="SCR" data-states="Arkansas,Louisiana,Mississippi,Oklahoma,Texas" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-south-central/379032">South Central</option>',
  1135. '<option value="Great Lakes" data-abbr="GLR" data-states="Illinois,Indiana,Michigan,Ohio,Wisconsin" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-great-lakes/379001">Great Lakes</option>',
  1136. '<option value="South Atlantic" data-abbr="SAT" data-states="Kentucky,North Carolina,South Carolina,Tennessee" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-south-atlantic/379018">South Atlantic</option>',
  1137. '<option value="Southeast" data-abbr="SER" data-states="Alabama,Florida,Georgia" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-southeast/379033">Southeast</option>',
  1138. '<option value="New England" data-abbr="NER" data-states="Connecticut,Maine,Massachusetts,New Hampshire,Rhode Island,Vermont" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-new-england/378990">New England</option>',
  1139. '<option value="Northeast" data-abbr="NOR" data-states="Delaware,New Jersey,New York,Pennsylvania" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-northeast/378995">Northeast</option>',
  1140. '<option value="Mid Atlantic" data-abbr="MAR" data-states="District of Columbia,Maryland,Virginia,West Virginia" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-mid-atlantic/378978">Mid Atlantic</option>',
  1141. '<option value="Territories" data-abbr="ATR" data-states="American Samoa,Guam,Puerto Rico,South Pacific Territories,US Virgin Islands" data-forum="" data-wiki="https://www.waze.com/discuss/t/usa-territories/379027">Territories</option>',
  1142. "</select>",
  1143. ].join(" ")
  1144. );
  1145. return $places.html();
  1146. }
  1147.  
  1148. function LoadStatesObj() {
  1149. if (debug) console.log(`${SCRIPT_NAME}: LoadStatesObj() called`);
  1150. States.Alabama = {
  1151. forum: "https://www.waze.com/discuss/c/editors/united-states/alabama/4839",
  1152. wiki: "https://www.waze.com/discuss/t/usa/377944",
  1153. abbr: "AL",
  1154. };
  1155. States.Alaska = {
  1156. forum: "https://www.waze.com/discuss/c/editors/united-states/alaska/4840",
  1157. wiki: "https://www.waze.com/discuss/t/alaska/377724",
  1158. abbr: "AK",
  1159. };
  1160. States.Arizona = {
  1161. forum: "https://www.waze.com/discuss/c/editors/united-states/arizona/4841",
  1162. wiki: "https://www.waze.com/discuss/t/arizona/377756",
  1163. abbr: "AZ",
  1164. };
  1165. States.Arkansas = {
  1166. forum: "https://www.waze.com/discuss/c/editors/united-states/arkansas/4842",
  1167. wiki: "https://www.waze.com/discuss/t/arkansas/377785",
  1168. abbr: "AR",
  1169. };
  1170. States.California = {
  1171. forum: "https://www.waze.com/discuss/c/editors/united-states/california/4843",
  1172. wiki: "https://www.waze.com/discuss/t/california/377832",
  1173. abbr: "CA",
  1174. };
  1175. States.Colorado = {
  1176. forum: "https://www.waze.com/discuss/c/editors/united-states/colorado/4844",
  1177. wiki: "https://www.waze.com/discuss/t/colorado/377867",
  1178. abbr: "CO",
  1179. };
  1180. States.Connecticut = {
  1181. forum: "https://www.waze.com/discuss/c/editors/united-states/connecticut/4845",
  1182. wiki: "https://www.waze.com/discuss/t/connecticut/377887",
  1183. abbr: "CT",
  1184. };
  1185. States.Delaware = {
  1186. forum: "https://www.waze.com/discuss/c/editors/united-states/delaware/4846",
  1187. wiki: "https://www.waze.com/discuss/t/delaware/377914",
  1188. abbr: "DE",
  1189. };
  1190. States["District of Columbia"] = {
  1191. forum: "https://www.waze.com/discuss/c/editors/united-states/district-of-columbia/4847",
  1192. wiki: "https://www.waze.com/discuss/t/district-of-columbia/377933",
  1193. abbr: "DC",
  1194. };
  1195. States.Florida = {
  1196. forum: "https://www.waze.com/discuss/c/editors/united-states/florida/4848",
  1197. wiki: "https://www.waze.com/discuss/t/usa-southeast/379033",
  1198. abbr: "FL",
  1199. };
  1200. States.Georgia = {
  1201. forum: "https://www.waze.com/discuss/c/editors/united-states/georgia/4849",
  1202. wiki: "https://www.waze.com/discuss/t/usa-southeast/379033",
  1203. abbr: "GA",
  1204. };
  1205. States.Hawaii = {
  1206. forum: "https://www.waze.com/discuss/c/editors/united-states/hawaii/4850",
  1207. wiki: "https://www.waze.com/discuss/t/hawaii/378021",
  1208. abbr: "HI",
  1209. };
  1210. States.Idaho = {
  1211. forum: "https://www.waze.com/discuss/c/editors/united-states/idaho/4851",
  1212. wiki: "https://www.waze.com/discuss/t/idaho/378060",
  1213. abbr: "ID",
  1214. };
  1215. States.Illinois = {
  1216. forum: "https://www.waze.com/discuss/c/editors/united-states/illinois/4852",
  1217. wiki: "https://www.waze.com/discuss/t/illinois/378077",
  1218. abbr: "IL",
  1219. };
  1220. States.Indiana = {
  1221. forum: "https://www.waze.com/discuss/c/editors/united-states/indiana/4853",
  1222. wiki: "https://www.waze.com/discuss/t/indiana/378111",
  1223. abbr: "IN",
  1224. };
  1225. States.Iowa = {
  1226. forum: "https://www.waze.com/discuss/c/editors/united-states/iowa/4854",
  1227. wiki: "https://www.waze.com/discuss/t/iowa/378125",
  1228. abbr: "IA",
  1229. };
  1230. States.Kansas = {
  1231. forum: "https://www.waze.com/discuss/c/editors/united-states/kansas/4855",
  1232. wiki: "https://www.waze.com/discuss/t/kansas/378134",
  1233. abbr: "KS",
  1234. };
  1235. States.Kentucky = {
  1236. forum: "https://www.waze.com/discuss/c/editors/united-states/kentucky/4856",
  1237. wiki: "https://www.waze.com/discuss/t/kentucky/378155",
  1238. abbr: "KY",
  1239. };
  1240. States.Louisiana = {
  1241. forum: "https://www.waze.com/discuss/c/editors/united-states/louisiana/4857",
  1242. wiki: "https://www.waze.com/discuss/t/louisiana/378174",
  1243. abbr: "LA",
  1244. };
  1245. States.Maine = {
  1246. forum: "https://www.waze.com/discuss/c/editors/united-states/maine/4858",
  1247. wiki: "https://www.waze.com/discuss/t/maine/378206",
  1248. abbr: "ME",
  1249. };
  1250. States.Maryland = {
  1251. forum: "https://www.waze.com/discuss/c/editors/united-states/maryland/4859",
  1252. wiki: "https://www.waze.com/discuss/t/maryland/378225",
  1253. abbr: "MD",
  1254. };
  1255. States.Massachusetts = {
  1256. forum: "https://www.waze.com/discuss/c/editors/united-states/massachusetts/4860",
  1257. wiki: "https://www.waze.com/discuss/t/massachusetts/378224",
  1258. abbr: "MA",
  1259. };
  1260. States.Michigan = {
  1261. forum: "https://www.waze.com/discuss/c/editors/united-states/michigan/4861",
  1262. wiki: "https://www.waze.com/discuss/t/michigan/378213",
  1263. abbr: "MI",
  1264. };
  1265. States.Minnesota = {
  1266. forum: "https://www.waze.com/discuss/c/editors/united-states/minnesota/4862",
  1267. wiki: "https://www.waze.com/discuss/t/minnesota/378223",
  1268. abbr: "MN",
  1269. };
  1270. States.Mississippi = {
  1271. forum: "https://www.waze.com/discuss/c/editors/united-states/mississippi/4863",
  1272. wiki: "https://www.waze.com/discuss/t/mississippi/378226",
  1273. abbr: "MS",
  1274. };
  1275. States.Missouri = {
  1276. forum: "https://www.waze.com/discuss/c/editors/united-states/missouri/4864",
  1277. wiki: "https://www.waze.com/discuss/t/missouri/378227",
  1278. abbr: "MO",
  1279. };
  1280. States.Montana = {
  1281. forum: "https://www.waze.com/discuss/c/editors/united-states/montana/4865",
  1282. wiki: "https://www.waze.com/discuss/t/montana/378228",
  1283. abbr: "MT",
  1284. };
  1285. States.Nebraska = {
  1286. forum: "https://www.waze.com/discuss/c/editors/united-states/nebraska/4866",
  1287. wiki: "https://www.waze.com/discuss/t/nebraska/378397",
  1288. abbr: "NE",
  1289. };
  1290. States.Nevada = {
  1291. forum: "https://www.waze.com/discuss/c/editors/united-states/nevada/4867",
  1292. wiki: "https://www.waze.com/discuss/t/nevada/378399",
  1293. abbr: "NV",
  1294. };
  1295. States["New Hampshire"] = {
  1296. forum: "https://www.waze.com/discuss/c/editors/united-states/New-Hampshire/4868",
  1297. wiki: "https://www.waze.com/discuss/t/new-hampshire/378422",
  1298. abbr: "NH",
  1299. };
  1300. States["New Jersey"] = {
  1301. forum: "https://www.waze.com/discuss/c/editors/united-states/new-jersey/4869",
  1302. wiki: "https://www.waze.com/discuss/t/new-jersey/378446",
  1303. abbr: "NJ",
  1304. };
  1305. States["New Mexico"] = {
  1306. forum: "https://www.waze.com/discuss/c/editors/united-states/new-mexico/4870",
  1307. wiki: "https://www.waze.com/discuss/t/new-mexico/378462",
  1308. abbr: "NM",
  1309. };
  1310. States["New York"] = {
  1311. forum: "https://www.waze.com/discuss/c/editors/united-states/new-york/4871",
  1312. wiki: "https://www.waze.com/discuss/t/new-york/378477",
  1313. abbr: "NY",
  1314. };
  1315. States["North Carolina"] = {
  1316. forum: "https://www.waze.com/discuss/c/editors/united-states/north-carolina/4872",
  1317. wiki: "https://www.waze.com/discuss/t/south-carolina/378795",
  1318. abbr: "NC",
  1319. };
  1320. States["North Dakota"] = {
  1321. forum: "https://www.waze.com/discuss/c/editors/united-states/north-dakota/4873",
  1322. wiki: "https://www.waze.com/discuss/t/north-dakota/378534",
  1323. abbr: "ND",
  1324. };
  1325. States.Ohio = {
  1326. forum: "https://www.waze.com/discuss/c/editors/united-states/ohio/4874",
  1327. wiki: "https://www.waze.com/discuss/t/ohio/378559",
  1328. abbr: "OH",
  1329. };
  1330. States.Oklahoma = {
  1331. forum: "https://www.waze.com/discuss/c/editors/united-states/oklahoma/4875",
  1332. wiki: "https://www.waze.com/discuss/t/oklahoma/378576",
  1333. abbr: "OK",
  1334. };
  1335. States.Oregon = {
  1336. forum: "https://www.waze.com/discuss/c/editors/united-states/oregon/4876",
  1337. wiki: "https://www.waze.com/discuss/t/oregon/378592",
  1338. abbr: "OR",
  1339. };
  1340. States.Pennsylvania = {
  1341. forum: "https://www.waze.com/discuss/c/editors/united-states/pennsylvania/4877",
  1342. wiki: "https://www.waze.com/discuss/t/pennsylvania/378626",
  1343. abbr: "PA",
  1344. };
  1345. States["Rhode Island"] = {
  1346. forum: "https://www.waze.com/discuss/c/editors/united-states/rhode-island/4880",
  1347. wiki: "https://www.waze.com/discuss/t/rhode-island/378697",
  1348. abbr: "RI",
  1349. };
  1350. States["South Carolina"] = {
  1351. forum: "https://www.waze.com/discuss/c/editors/united-states/south-carolina/4881",
  1352. wiki: "https://www.waze.com/discuss/t/south-carolina/378795",
  1353. abbr: "SC",
  1354. };
  1355. States["South Dakota"] = {
  1356. forum: "https://www.waze.com/discuss/c/editors/united-states/south-dakota/4882",
  1357. wiki: "https://www.waze.com/discuss/t/south-dakota/378798",
  1358. abbr: "SD",
  1359. };
  1360. States.Tennessee = {
  1361. forum: "https://www.waze.com/discuss/c/editors/united-states/tennessee/4884",
  1362. wiki: "https://www.waze.com/discuss/t/tennessee/378849",
  1363. abbr: "TN",
  1364. };
  1365. States.Texas = {
  1366. forum: "https://www.waze.com/discuss/c/editors/united-states/texas/4885",
  1367. wiki: "https://www.waze.com/discuss/t/texas/378229",
  1368. abbr: "TX",
  1369. };
  1370. States.Utah = {
  1371. forum: "https://www.waze.com/discuss/c/editors/united-states/utah/4895",
  1372. wiki: "https://www.waze.com/discuss/t/utah/379059",
  1373. abbr: "UT",
  1374. };
  1375. States.Vermont = {
  1376. forum: "https://www.waze.com/discuss/c/editors/united-states/vermont/4896",
  1377. wiki: "https://www.waze.com/discuss/t/vermont/379061",
  1378. abbr: "VT",
  1379. };
  1380. States.Virginia = {
  1381. forum: "https://www.waze.com/discuss/c/editors/united-states/virginia/4897",
  1382. wiki: "https://www.waze.com/discuss/t/virginia/379094",
  1383. abbr: "VA",
  1384. };
  1385. States.Washington = {
  1386. forum: "https://www.waze.com/discuss/c/editors/united-states/washington/4898",
  1387. wiki: "https://www.waze.com/discuss/t/washington/379099",
  1388. abbr: "WA",
  1389. };
  1390. States["West Virginia"] = {
  1391. forum: "https://www.waze.com/discuss/c/editors/united-states/west-virginia/4899",
  1392. wiki: "https://www.waze.com/discuss/t/west-virginia/379151",
  1393. abbr: "WV",
  1394. };
  1395. States.Wisconsin = {
  1396. forum: "https://www.waze.com/discuss/c/editors/united-states/wisconsin/4900",
  1397. wiki: "https://www.waze.com/discuss/t/wisconsin/379101",
  1398. abbr: "WI",
  1399. };
  1400. States.Wyoming = {
  1401. forum: "https://www.waze.com/discuss/c/editors/united-states/wyoming/4901",
  1402. wiki: "https://www.waze.com/discuss/t/wyoming/379211",
  1403. abbr: "WY",
  1404. };
  1405. States["Puerto Rico"] = {
  1406. forum: "https://www.waze.com/discuss/c/editors/united-states/puerto-rico/4879",
  1407. wiki: "https://www.waze.com/discuss/t/puerto-rico/378675",
  1408. abbr: "PR",
  1409. };
  1410. States["US Virgin Islands"] = {
  1411. forum: "https://www.waze.com/discuss/c/editors/united-states/us-virgin-islands/4892",
  1412. wiki: "hhttps://www.waze.com/discuss/t/virgin-islands/379085",
  1413. abbr: "VI",
  1414. };
  1415. States["South Pacific Territories"] = {
  1416. forum: "https://www.waze.com/discuss/c/editors/united-states/south-pacific-territories/4883",
  1417. wiki: "",
  1418. abbr: "SP",
  1419. };
  1420. States["American Samoa"] = {
  1421. forum: "",
  1422. wiki: "https://www.waze.com/discuss/t/american-samoa/377744",
  1423. abbr: "AS",
  1424. };
  1425. States["Guam"] = {
  1426. forum: "",
  1427. wiki: "https://www.waze.com/discuss/t/guam/378007",
  1428. abbr: "GU",
  1429. };
  1430. }
  1431.  
  1432. function injectCss() {
  1433. if (debug) console.log(`${SCRIPT_NAME}: injectCss() called`);
  1434. var css = [
  1435. // General text styling for WazeBar elements
  1436. ".WazeBarText { display: inline; padding-right: 4px; margin-left: 4px; border-right: thin solid grey; font-size: " + WazeBarSettings.BarFontSize + "px; color: #555555;}",
  1437. // WazeBar Forum / Wiki / Current State Forum & Wiki links styling
  1438. ".WazeBarText.WazeBarWikiItem a { color: " + WazeBarSettings.WikiFontColor + "; }",
  1439. ".WazeBarText.WazeBarForumItem a { color: " + WazeBarSettings.ForumFontColor + "; }",
  1440. ".WazeBarText.WazeBarCurrState a { color: #FF0000; }",
  1441. ".WazeBarText.WazeBarServerUpdate {}",
  1442. ".WazeBarIcon { display: inline; margin-left: 8px; cursor: pointer; font-size: " + WazeBarSettings.BarFontSize + "px; color: #555555;}",
  1443.  
  1444. // WazeBar styling
  1445. // WazeBar Favorites dropdown styling
  1446. "#WazeBarFavorites { max-height: 500px; z-index: 100; overflow: auto; display: none; position: absolute; background-color: #f9f9f9; min-width: 200px; margin-top: -2px; padding: 10px; }",
  1447. "#WazeBarFavoritesList { list-style: none; padding: 0; margin: 0; }",
  1448. ".favorite-item { position: relative; padding: 4px 4px; margin: 4px 0; background: #f1f1f1; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; }",
  1449. ".favorite-item a { flex-grow: 1; text-decoration: none; color: #555555; }",
  1450. ".favorite-item a:visited { color: #555555; }",
  1451. ".favorite-item i { cursor: pointer; color: #c00; }",
  1452. ".favorite-item:hover { background: #33CCFF; }",
  1453. "#WazeBarFavoritesAddContainer { display: flex; flex-direction: column; margin-top: 10px; gap: 8px; }",
  1454. "#WazeBarFavoritesAddContainer input { height: 25px; border: 1px solid #000000; padding: 4px; border-radius: 6px; background-color: white}",
  1455. "#WazeBarAddFavorite { padding: 8px 8px; font-size: 1rem; background-color: #8BC34A; color: white; border: 2px solid #8BC34A; border-radius: 25px; cursor: pointer; box-sizing: border-box; transition: background-color 0.3s ease, border-color 0.3s ease; }",
  1456. "#WazeBarAddFavorite:hover { background-color: #689F38; border-color: #689F38; }",
  1457.  
  1458. // Unread messages popup delay styling
  1459. ".WazeBarUnread { max-height: 500px; z-index: 100; overflow: auto; display: flex; position: absolute; background-color: #f9f9f9; min-width: 200px; max-width: 700px; margin-top: -2px; padding: 8px; }",
  1460. ".WazeBarUnreadList { list-style: none; padding: 0; margin: 0; }",
  1461. ".WazeBarUnreadList.unread-item { position: relative; padding: 4px 4px; margin: 4px 0; background: #f1f1f1; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; }",
  1462. ".WazeBarUnreadList.unread-item a { flex-grow: 1; text-decoration: none; color: #333; font-weight: normal;}",
  1463. ".WazeBarUnreadList.unread-item i { cursor: pointer; color: #c00; }",
  1464. ".WazeBarUnreadList.unread-item:hover { background: #33CCFF; }",
  1465.  
  1466. // Main Setting Menu diolog
  1467. "#WazeBarSettings {cursor: move; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #fff; border: 3px solid #000; border-radius: 5%; padding: 12px; color: #000000;}",
  1468. "#WazeBarSettings input[type='number'], #WazeBarSettings input[type='text'], #WazeBarSettings textarea { border: 1px solid #000; padding: 5px; border-radius: 6px; margin-bottom: 0px; background-color: white; color: #000000; }",
  1469. "#WazeBarSettings button { padding: 8px 12px; border: none; border-radius: 25px; cursor: pointer; }",
  1470. "#WazeBarSettings button#WBSettingsSave { background-color: #007bff; color: #fff; }",
  1471. "#WazeBarSettings button#WBSettingsSave:hover { background-color: #0056b3; }",
  1472. "#WazeBarSettings button#WBSettingsCancel { background-color: #6c757d; color: #fff; }",
  1473. "#WazeBarSettings button#WBSettingsCancel:hover { background-color: #5a6268; }",
  1474. "#WazeBarSettings h4 { margin-top: 4px; margin-bottom: 4px; font-size: 14px; line-height: 1.2; text-align: center; }",
  1475. "#WazeBarSettings #customLinksSection { margin-top: 5px; }",
  1476. "#WazeBarSettings #customLinksSection div { margin-bottom: 0; }",
  1477. "#WazeBarSettings label { font-weight: normal; margin-bottom: auto;}",
  1478. // Inline element alignment for the settings inputs
  1479. "#WazeBarSettings .flex-row { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }",
  1480.  
  1481. // Flex container holds the flex columns on the Main Setting Menu diolog
  1482. ".flex-container { display: flex; align-items: flex-start; width: 100%; gap: 8px; box-sizing: border-box; height: 490px; overflow-y: auto;}",
  1483. ".flex-column { padding: 8px; position: relative; box-sizing: border-box; border: 1px solid #ccc; background-color: #f9f9f9; width: 230px; flex: 1 1 auto; min-height: 490px; border-radius: 1%;}",
  1484. ".left-column::after { content: ''; position: absolute; top: 0; right: 0; width: 0px; height: 100%; background-color: #ccc; }",
  1485. ".right-column::before { content: ''; position: absolute; top: 0; left: 0; width: 0px; height: 100%; background-color: #ccc; }",
  1486.  
  1487. // Color Picker styling for Forumn and Wiki links
  1488. "#colorPickerForumFont, #colorPickerWikiFont {border: 1px solid #000000; border-radius: 6px; background-color: white; }",
  1489.  
  1490. // State rows styling
  1491. ".state-row { display: flex; align-items: center; }",
  1492. ".state-row div { padding: 4px 4px; }",
  1493. ".checkbox-column { display: flex; justify-content: center; align-items: center; }",
  1494. // State Table header styling
  1495. ".state-header { display: inline-flex; }",
  1496. ".state-header div { padding: 8px; }",
  1497. // State Flex-box for the table
  1498. ".state-column { flex: 6; }",
  1499. ".checkbox-column { flex: 1; }",
  1500.  
  1501. // Horizontal rule styling
  1502. "hr { border: none; border-top: 1px solid #ccc; margin: 10px 0 0 0; width: calc(100% - 16px); }",
  1503.  
  1504. // Additional styles for Custom Links section inputs to match Favorites section inputs
  1505. "#WazeBarCustomURL, #WazeBarCustomText, #WazeBarAddCustomLink { box-sizing: border-box; width: 100%; margin: 5px; }",
  1506. "#WazeBarCustomURL, #WazeBarCustomText { height: 25px; border: 1px solid #000000; padding: 8px; border-radius: 6px; margin-bottom: 3px; }",
  1507. "#WazeBarAddCustomLink { padding: 8px 0; font-size: 1rem; background-color: #8BC34A; color: white; border: 2px solid #8BC34A; border-radius: 6px; cursor: pointer; transition: background-color 0.3s ease, border-color 0.3s ease; }",
  1508. "#WazeBarAddCustomLink:hover { background-color: #689F38; border-color: #689F38; }",
  1509.  
  1510. // Custom List link styling
  1511. "#WazeBarCustomLinksList { list-style: none; padding: 0; margin: 0; }",
  1512. ".custom-item { position: relative; padding: 4px; margin: 4px 0; background: #f1f1f1; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; width: 100%; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); transition: background 0.3s ease, transform 0.3s ease; border: 1px solid #ddd; }",
  1513. ".custom-item a { display: block; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-decoration: none; color: #555555; }",
  1514. ".custom-item a:visited { color: #555555; }",
  1515. ".custom-item i { cursor: pointer; color: #f56a6a; }",
  1516. ".custom-item:hover { background: #33CCFF; }",
  1517. ".custom-item i:hover { }",
  1518.  
  1519. // Export/Import Section Styling
  1520. ".flex-row { display: flex; align-items: center; gap: 5px; margin-bottom: 5px; }",
  1521. ".export-button, .import-button { font-size: 1.5rem; padding: 10px; background-color: #007bff; color: white; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.3s ease, transform 0.3s ease; }",
  1522. ".export-button:hover, .import-button:hover { background-color: #0056b3; transform: scale(1.05); }",
  1523. "#txtWazebarSettings, #txtWazebarImportSettings { width: 100%; height: 140px; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 12px; box-sizing: border-box; resize: none; }",
  1524.  
  1525. // Ensure textareas align properly in flex container
  1526. ".flex-row textarea { flex-grow: 0; }",
  1527.  
  1528. // Adjust Export and Import button font sizes for better alignment
  1529. ".fa-upload, .fa-download { font-size: 1.2rem; padding: 10px; }",
  1530.  
  1531. // styling for checkbox containers to align labels and checkboxes
  1532. ".checkbox-container { display: flex; align-items: center; margin-bottom: 4px; }",
  1533. ".checkbox-container input[type='checkbox'] { margin-right: 8px; }",
  1534.  
  1535. // Custom styling for the region dropdown
  1536. ".styled-select { width: 95%; border-radius: 6px; }",
  1537. ".styled-select:focus { border-color: #007bff; box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25); outline: none; }",
  1538. ].join(" ");
  1539.  
  1540. // Remove the previous styles if they exist
  1541. $("#WazeBarStyles").remove();
  1542.  
  1543. // Append the new styles
  1544. $('<style type="text/css" id="WazeBarStyles">' + css + "</style>").appendTo("head");
  1545. }
  1546.  
  1547. // Call the function to inject the CSS
  1548. injectCss();
  1549.  
  1550. function isChecked(checkboxId) {
  1551. return $("#" + checkboxId).is(":checked");
  1552. }
  1553.  
  1554. function setChecked(checkboxId, checked) {
  1555. $("#" + checkboxId).prop("checked", checked);
  1556. }
  1557.  
  1558. const defaultSettings = {
  1559. forumInterval: 2,
  1560. forumHistory: 7,
  1561. scriptsForum: false,
  1562. header: { region: {} },
  1563. USSMForum: false,
  1564. USChampForum: false,
  1565. USWikiForum: false,
  1566. NAServerUpdate: true,
  1567. WMEBetaForum: false,
  1568. DisplayWazeForum: false,
  1569. Favorites: [
  1570. {
  1571. href: "https://www.waze.com/discuss/t/waze-map-editor-welcome/379135",
  1572. text: "Map Editor Welcome",
  1573. },
  1574. {
  1575. href: "https://www.waze.com/discuss/t/waze-etiquette/378211",
  1576. text: "Etiquette",
  1577. },
  1578. {
  1579. href: "https://www.waze.com/discuss/t/glossary/377948",
  1580. text: "Glossary",
  1581. },
  1582. ],
  1583. ForumFontColor: "#1E90FF",
  1584. WikiFontColor: "#32CD32",
  1585. BarFontSize: 13,
  1586. CustomLinks: [],
  1587. ROWServerUpdate: false,
  1588. };
  1589.  
  1590. function LoadSettingsObj() {
  1591. if (debug) console.log(`${SCRIPT_NAME}: LoadSettingsObj() called:`);
  1592.  
  1593. let loadedSettings;
  1594. try {
  1595. loadedSettings = JSON.parse(localStorage.getItem("Wazebar_Settings"));
  1596. } catch (err) {
  1597. loadedSettings = null;
  1598. }
  1599.  
  1600. WazeBarSettings = loadedSettings ? loadedSettings : { ...defaultSettings };
  1601.  
  1602. for (const prop in defaultSettings) {
  1603. if (!WazeBarSettings.hasOwnProperty(prop)) WazeBarSettings[prop] = defaultSettings[prop];
  1604. }
  1605. }
  1606.  
  1607. function serializeSettings() {
  1608. SaveSettings(); // Save current settings to localStorage
  1609. const settings = JSON.parse(localStorage.getItem("Wazebar_Settings")) || {};
  1610. const serialized = JSON.stringify(settings, null, 2); // Pretty print JSON with 2 spaces indentation
  1611. // Update #txtWazebarSettings with the serialized settings
  1612. $("#txtWazebarSettings").text(serialized);
  1613. return serialized;
  1614. }
  1615. // Function to update WazeBarSettings from the UI elements
  1616. function updateWazeBarSettingsFromUI() {
  1617. WazeBarSettings.DisplayWazeForum = isChecked("WazeForumSetting");
  1618. WazeBarSettings.WMEBetaForum = isChecked("WMEBetaForumSetting");
  1619. WazeBarSettings.scriptsForum = isChecked("ScriptsForum");
  1620. WazeBarSettings.USSMForum = isChecked("USSMForumSetting");
  1621. if (!forumPage) {
  1622. WazeBarSettings.USChampForum = isChecked("USChampForumSetting");
  1623. }
  1624. WazeBarSettings.USWikiForum = isChecked("USWikiForumSetting");
  1625. WazeBarSettings.ForumFontColor = $("#colorPickerForumFont").val();
  1626. WazeBarSettings.WikiFontColor = $("#colorPickerWikiFont").val();
  1627. WazeBarSettings.forumInterval = $("#forumInterval").val();
  1628. WazeBarSettings.forumHistory = $("#forumHistory").val();
  1629. WazeBarSettings.NAServerUpdate = isChecked("NAServerUpdateSetting");
  1630. WazeBarSettings.ROWServerUpdate = isChecked("ROWServerUpdateSetting");
  1631. WazeBarSettings.BarFontSize = $("#WazeBarFontSize").val();
  1632. if (WazeBarSettings.BarFontSize < 8) {
  1633. WazeBarSettings.BarFontSize = 8;
  1634. $("#WazeBarFontSize").val(8);
  1635. }
  1636. }
  1637.  
  1638. function SaveSettings() {
  1639. if (debug) console.log(`${SCRIPT_NAME}: SaveSettings() called:`);
  1640. if (localStorage) {
  1641. var localsettings = {
  1642. BarFontSize: WazeBarSettings.BarFontSize,
  1643. ForumFontColor: WazeBarSettings.ForumFontColor,
  1644. WikiFontColor: WazeBarSettings.WikiFontColor,
  1645. forumInterval: WazeBarSettings.forumInterval,
  1646. forumHistory: WazeBarSettings.forumHistory,
  1647. DisplayWazeForum: WazeBarSettings.DisplayWazeForum,
  1648. WMEBetaForum: WazeBarSettings.WMEBetaForum,
  1649. scriptsForum: WazeBarSettings.scriptsForum,
  1650. header: WazeBarSettings.header,
  1651. USChampForum: WazeBarSettings.USChampForum,
  1652. USSMForum: WazeBarSettings.USSMForum,
  1653. USWikiForum: WazeBarSettings.USWikiForum,
  1654. NAServerUpdate: WazeBarSettings.NAServerUpdate,
  1655. ROWServerUpdate: WazeBarSettings.ROWServerUpdate,
  1656. Favorites: WazeBarSettings.Favorites,
  1657. CustomLinks: WazeBarSettings.CustomLinks,
  1658. };
  1659. localStorage.setItem("Wazebar_Settings", JSON.stringify(localsettings));
  1660. }
  1661. }
  1662.  
  1663. function localAlertInfo(scriptName, message, disableTimeout = false) {
  1664. // Create the basic alert element
  1665. const alertHtml = `
  1666. <div class="toast-info">
  1667. <div class="toast-header">
  1668. <b>${scriptName} - Info</b>
  1669. <button type="button" class="toast-close-button">&times;</button>
  1670. </div>
  1671. <div class="toast-message">
  1672. <br>
  1673. ${message}
  1674. </div>
  1675. </div>
  1676. `;
  1677.  
  1678. // Create the alert element and style it to be centered in the window
  1679. const $alertElement = $(alertHtml).css({
  1680. position: "fixed",
  1681. top: "50%",
  1682. left: "50%",
  1683. transform: "translate(-50%, -50%)",
  1684. backgroundColor: "#007bff",
  1685. color: "#fff",
  1686. padding: "15px",
  1687. borderRadius: "10px",
  1688. boxShadow: "0 0 10px rgba(0, 0, 0, 0.1)",
  1689. zIndex: 1000, // Ensure it's on top of other content
  1690. });
  1691.  
  1692. // Style for the close button
  1693. $alertElement.find(".toast-close-button").css({
  1694. position: "absolute",
  1695. top: "0px",
  1696. right: "5px",
  1697. fontSize: "25px",
  1698. color: "#fff",
  1699. background: "none",
  1700. border: "none",
  1701. cursor: "pointer",
  1702. });
  1703.  
  1704. // Append the alert element to the specified container
  1705.  
  1706. if (forumPage) {
  1707. $("body").append($alertElement);
  1708. } else {
  1709. $("#waze-map-container").append($alertElement);
  1710. }
  1711.  
  1712. // Handle the close button functionality
  1713. $alertElement.find(".toast-close-button").on("click", function () {
  1714. $alertElement.remove();
  1715. });
  1716.  
  1717. // Auto-dismiss if disableTimeout is not true
  1718. if (!disableTimeout) {
  1719. setTimeout(() => {
  1720. $alertElement.fadeOut(() => $alertElement.remove());
  1721. }, 3000); // 5-second timeout by default
  1722. }
  1723. }
  1724. })();