Greasy Fork is available in English.

WME WazeMY

WazeMY beta

  1. // ==UserScript==
  2. // @name WME WazeMY
  3. // @namespace https://www.github.com/junyian/
  4. // @version 2024.11.29.01
  5. // @author junyianl <junyian@gmail.com>
  6. // @source https://github.com/junyian/wme-wazemy
  7. // @license MIT
  8. // @match *://www.waze.com/editor*
  9. // @match *://www.waze.com/*/editor*
  10. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  11. // @require https://greasyfork.org/scripts/449165-wme-wazemy-trafcamlist/code/wme-wazemy-trafcamlist.js
  12. // @grant GM_xmlhttpRequest
  13. // @grant GM.xmlHttpRequest
  14. // @connect p3.fgies.com
  15. // @connect p4.fgies.com
  16. // @connect t2.fgies.com
  17. // @connect jalanow.com
  18. // @connect llm.gov.my
  19. // @run-at document-end
  20. // @description WazeMY beta
  21. // ==/UserScript==
  22.  
  23. /******/ (() => { // webpackBootstrap
  24. /******/ "use strict";
  25. /******/ var __webpack_modules__ = ({
  26.  
  27. /***/ "./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/style/main.less":
  28. /***/ ((module, __webpack_exports__, __webpack_require__) => {
  29.  
  30. /* harmony export */ __webpack_require__.d(__webpack_exports__, {
  31. /* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__)
  32. /* harmony export */ });
  33. /* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./node_modules/css-loader/dist/runtime/noSourceMaps.js");
  34. /* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
  35. /* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./node_modules/css-loader/dist/runtime/api.js");
  36. /* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
  37. // Imports
  38.  
  39.  
  40. var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
  41. // Module
  42. ___CSS_LOADER_EXPORT___.push([module.id, `.wazemySettings {
  43. border: 1px solid;
  44. padding: 8px;
  45. border-radius: 4px;
  46. }
  47. .wazemySettings legend {
  48. margin-bottom: 0px;
  49. border-bottom-style: none;
  50. width: auto;
  51. }
  52. .wazemySettings h6 {
  53. margin-bottom: 0px;
  54. }
  55. .wazemySettings input {
  56. margin-top: 0px;
  57. }
  58. #wazemyTooltip {
  59. height: auto;
  60. width: auto;
  61. background-color: rgba(0, 0, 0, 0.5);
  62. color: white;
  63. border-radius: 4px;
  64. padding: 4px;
  65. position: absolute;
  66. top: 0px;
  67. left: 0px;
  68. visibility: hidden;
  69. z-index: 10000;
  70. }
  71. #wazemyPlaces_table {
  72. overflow-x: scroll;
  73. }
  74. #wazemyPlaces_venues {
  75. width: 95%;
  76. border: 1px solid;
  77. }
  78. #wazemyPlaces_venues th {
  79. border: 1px solid;
  80. background-color: #ccc;
  81. }
  82. #wazemyPlaces_venues td {
  83. border: 1px solid;
  84. }
  85. `, ""]);
  86. // Exports
  87. /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);
  88.  
  89.  
  90. /***/ }),
  91.  
  92. /***/ "./node_modules/css-loader/dist/runtime/api.js":
  93. /***/ ((module) => {
  94.  
  95.  
  96.  
  97. /*
  98. MIT License http://www.opensource.org/licenses/mit-license.php
  99. Author Tobias Koppers @sokra
  100. */
  101. module.exports = function (cssWithMappingToString) {
  102. var list = [];
  103.  
  104. // return the list of modules as css string
  105. list.toString = function toString() {
  106. return this.map(function (item) {
  107. var content = "";
  108. var needLayer = typeof item[5] !== "undefined";
  109. if (item[4]) {
  110. content += "@supports (".concat(item[4], ") {");
  111. }
  112. if (item[2]) {
  113. content += "@media ".concat(item[2], " {");
  114. }
  115. if (needLayer) {
  116. content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
  117. }
  118. content += cssWithMappingToString(item);
  119. if (needLayer) {
  120. content += "}";
  121. }
  122. if (item[2]) {
  123. content += "}";
  124. }
  125. if (item[4]) {
  126. content += "}";
  127. }
  128. return content;
  129. }).join("");
  130. };
  131.  
  132. // import a list of modules into the list
  133. list.i = function i(modules, media, dedupe, supports, layer) {
  134. if (typeof modules === "string") {
  135. modules = [[null, modules, undefined]];
  136. }
  137. var alreadyImportedModules = {};
  138. if (dedupe) {
  139. for (var k = 0; k < this.length; k++) {
  140. var id = this[k][0];
  141. if (id != null) {
  142. alreadyImportedModules[id] = true;
  143. }
  144. }
  145. }
  146. for (var _k = 0; _k < modules.length; _k++) {
  147. var item = [].concat(modules[_k]);
  148. if (dedupe && alreadyImportedModules[item[0]]) {
  149. continue;
  150. }
  151. if (typeof layer !== "undefined") {
  152. if (typeof item[5] === "undefined") {
  153. item[5] = layer;
  154. } else {
  155. item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
  156. item[5] = layer;
  157. }
  158. }
  159. if (media) {
  160. if (!item[2]) {
  161. item[2] = media;
  162. } else {
  163. item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
  164. item[2] = media;
  165. }
  166. }
  167. if (supports) {
  168. if (!item[4]) {
  169. item[4] = "".concat(supports);
  170. } else {
  171. item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
  172. item[4] = supports;
  173. }
  174. }
  175. list.push(item);
  176. }
  177. };
  178. return list;
  179. };
  180.  
  181. /***/ }),
  182.  
  183. /***/ "./node_modules/css-loader/dist/runtime/noSourceMaps.js":
  184. /***/ ((module) => {
  185.  
  186.  
  187.  
  188. module.exports = function (i) {
  189. return i[1];
  190. };
  191.  
  192. /***/ }),
  193.  
  194. /***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":
  195. /***/ ((module) => {
  196.  
  197.  
  198.  
  199. var stylesInDOM = [];
  200. function getIndexByIdentifier(identifier) {
  201. var result = -1;
  202. for (var i = 0; i < stylesInDOM.length; i++) {
  203. if (stylesInDOM[i].identifier === identifier) {
  204. result = i;
  205. break;
  206. }
  207. }
  208. return result;
  209. }
  210. function modulesToDom(list, options) {
  211. var idCountMap = {};
  212. var identifiers = [];
  213. for (var i = 0; i < list.length; i++) {
  214. var item = list[i];
  215. var id = options.base ? item[0] + options.base : item[0];
  216. var count = idCountMap[id] || 0;
  217. var identifier = "".concat(id, " ").concat(count);
  218. idCountMap[id] = count + 1;
  219. var indexByIdentifier = getIndexByIdentifier(identifier);
  220. var obj = {
  221. css: item[1],
  222. media: item[2],
  223. sourceMap: item[3],
  224. supports: item[4],
  225. layer: item[5]
  226. };
  227. if (indexByIdentifier !== -1) {
  228. stylesInDOM[indexByIdentifier].references++;
  229. stylesInDOM[indexByIdentifier].updater(obj);
  230. } else {
  231. var updater = addElementStyle(obj, options);
  232. options.byIndex = i;
  233. stylesInDOM.splice(i, 0, {
  234. identifier: identifier,
  235. updater: updater,
  236. references: 1
  237. });
  238. }
  239. identifiers.push(identifier);
  240. }
  241. return identifiers;
  242. }
  243. function addElementStyle(obj, options) {
  244. var api = options.domAPI(options);
  245. api.update(obj);
  246. var updater = function updater(newObj) {
  247. if (newObj) {
  248. if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {
  249. return;
  250. }
  251. api.update(obj = newObj);
  252. } else {
  253. api.remove();
  254. }
  255. };
  256. return updater;
  257. }
  258. module.exports = function (list, options) {
  259. options = options || {};
  260. list = list || [];
  261. var lastIdentifiers = modulesToDom(list, options);
  262. return function update(newList) {
  263. newList = newList || [];
  264. for (var i = 0; i < lastIdentifiers.length; i++) {
  265. var identifier = lastIdentifiers[i];
  266. var index = getIndexByIdentifier(identifier);
  267. stylesInDOM[index].references--;
  268. }
  269. var newLastIdentifiers = modulesToDom(newList, options);
  270. for (var _i = 0; _i < lastIdentifiers.length; _i++) {
  271. var _identifier = lastIdentifiers[_i];
  272. var _index = getIndexByIdentifier(_identifier);
  273. if (stylesInDOM[_index].references === 0) {
  274. stylesInDOM[_index].updater();
  275. stylesInDOM.splice(_index, 1);
  276. }
  277. }
  278. lastIdentifiers = newLastIdentifiers;
  279. };
  280. };
  281.  
  282. /***/ }),
  283.  
  284. /***/ "./node_modules/style-loader/dist/runtime/insertBySelector.js":
  285. /***/ ((module) => {
  286.  
  287.  
  288.  
  289. var memo = {};
  290.  
  291. /* istanbul ignore next */
  292. function getTarget(target) {
  293. if (typeof memo[target] === "undefined") {
  294. var styleTarget = document.querySelector(target);
  295.  
  296. // Special case to return head of iframe instead of iframe itself
  297. if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
  298. try {
  299. // This will throw an exception if access to iframe is blocked
  300. // due to cross-origin restrictions
  301. styleTarget = styleTarget.contentDocument.head;
  302. } catch (e) {
  303. // istanbul ignore next
  304. styleTarget = null;
  305. }
  306. }
  307. memo[target] = styleTarget;
  308. }
  309. return memo[target];
  310. }
  311.  
  312. /* istanbul ignore next */
  313. function insertBySelector(insert, style) {
  314. var target = getTarget(insert);
  315. if (!target) {
  316. throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
  317. }
  318. target.appendChild(style);
  319. }
  320. module.exports = insertBySelector;
  321.  
  322. /***/ }),
  323.  
  324. /***/ "./node_modules/style-loader/dist/runtime/insertStyleElement.js":
  325. /***/ ((module) => {
  326.  
  327.  
  328.  
  329. /* istanbul ignore next */
  330. function insertStyleElement(options) {
  331. var element = document.createElement("style");
  332. options.setAttributes(element, options.attributes);
  333. options.insert(element, options.options);
  334. return element;
  335. }
  336. module.exports = insertStyleElement;
  337.  
  338. /***/ }),
  339.  
  340. /***/ "./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js":
  341. /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
  342.  
  343.  
  344.  
  345. /* istanbul ignore next */
  346. function setAttributesWithoutAttributes(styleElement) {
  347. var nonce = true ? __webpack_require__.nc : 0;
  348. if (nonce) {
  349. styleElement.setAttribute("nonce", nonce);
  350. }
  351. }
  352. module.exports = setAttributesWithoutAttributes;
  353.  
  354. /***/ }),
  355.  
  356. /***/ "./node_modules/style-loader/dist/runtime/styleDomAPI.js":
  357. /***/ ((module) => {
  358.  
  359.  
  360.  
  361. /* istanbul ignore next */
  362. function apply(styleElement, options, obj) {
  363. var css = "";
  364. if (obj.supports) {
  365. css += "@supports (".concat(obj.supports, ") {");
  366. }
  367. if (obj.media) {
  368. css += "@media ".concat(obj.media, " {");
  369. }
  370. var needLayer = typeof obj.layer !== "undefined";
  371. if (needLayer) {
  372. css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
  373. }
  374. css += obj.css;
  375. if (needLayer) {
  376. css += "}";
  377. }
  378. if (obj.media) {
  379. css += "}";
  380. }
  381. if (obj.supports) {
  382. css += "}";
  383. }
  384. var sourceMap = obj.sourceMap;
  385. if (sourceMap && typeof btoa !== "undefined") {
  386. css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
  387. }
  388.  
  389. // For old IE
  390. /* istanbul ignore if */
  391. options.styleTagTransform(css, styleElement, options.options);
  392. }
  393. function removeStyleElement(styleElement) {
  394. // istanbul ignore if
  395. if (styleElement.parentNode === null) {
  396. return false;
  397. }
  398. styleElement.parentNode.removeChild(styleElement);
  399. }
  400.  
  401. /* istanbul ignore next */
  402. function domAPI(options) {
  403. if (typeof document === "undefined") {
  404. return {
  405. update: function update() {},
  406. remove: function remove() {}
  407. };
  408. }
  409. var styleElement = options.insertStyleElement(options);
  410. return {
  411. update: function update(obj) {
  412. apply(styleElement, options, obj);
  413. },
  414. remove: function remove() {
  415. removeStyleElement(styleElement);
  416. }
  417. };
  418. }
  419. module.exports = domAPI;
  420.  
  421. /***/ }),
  422.  
  423. /***/ "./node_modules/style-loader/dist/runtime/styleTagTransform.js":
  424. /***/ ((module) => {
  425.  
  426.  
  427.  
  428. /* istanbul ignore next */
  429. function styleTagTransform(css, styleElement) {
  430. if (styleElement.styleSheet) {
  431. styleElement.styleSheet.cssText = css;
  432. } else {
  433. while (styleElement.firstChild) {
  434. styleElement.removeChild(styleElement.firstChild);
  435. }
  436. styleElement.appendChild(document.createTextNode(css));
  437. }
  438. }
  439. module.exports = styleTagTransform;
  440.  
  441. /***/ })
  442.  
  443. /******/ });
  444. /************************************************************************/
  445. /******/ // The module cache
  446. /******/ var __webpack_module_cache__ = {};
  447. /******/
  448. /******/ // The require function
  449. /******/ function __webpack_require__(moduleId) {
  450. /******/ // Check if module is in cache
  451. /******/ var cachedModule = __webpack_module_cache__[moduleId];
  452. /******/ if (cachedModule !== undefined) {
  453. /******/ return cachedModule.exports;
  454. /******/ }
  455. /******/ // Create a new module (and put it into the cache)
  456. /******/ var module = __webpack_module_cache__[moduleId] = {
  457. /******/ id: moduleId,
  458. /******/ // no module.loaded needed
  459. /******/ exports: {}
  460. /******/ };
  461. /******/
  462. /******/ // Execute the module function
  463. /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  464. /******/
  465. /******/ // Return the exports of the module
  466. /******/ return module.exports;
  467. /******/ }
  468. /******/
  469. /************************************************************************/
  470. /******/ /* webpack/runtime/compat get default export */
  471. /******/ (() => {
  472. /******/ // getDefaultExport function for compatibility with non-harmony modules
  473. /******/ __webpack_require__.n = (module) => {
  474. /******/ var getter = module && module.__esModule ?
  475. /******/ () => (module['default']) :
  476. /******/ () => (module);
  477. /******/ __webpack_require__.d(getter, { a: getter });
  478. /******/ return getter;
  479. /******/ };
  480. /******/ })();
  481. /******/
  482. /******/ /* webpack/runtime/define property getters */
  483. /******/ (() => {
  484. /******/ // define getter functions for harmony exports
  485. /******/ __webpack_require__.d = (exports, definition) => {
  486. /******/ for(var key in definition) {
  487. /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
  488. /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
  489. /******/ }
  490. /******/ }
  491. /******/ };
  492. /******/ })();
  493. /******/
  494. /******/ /* webpack/runtime/hasOwnProperty shorthand */
  495. /******/ (() => {
  496. /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
  497. /******/ })();
  498. /******/
  499. /******/ /* webpack/runtime/nonce */
  500. /******/ (() => {
  501. /******/ __webpack_require__.nc = undefined;
  502. /******/ })();
  503. /******/
  504. /************************************************************************/
  505. var __webpack_exports__ = {};
  506. // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
  507. (() => {
  508.  
  509. // EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js
  510. var injectStylesIntoStyleTag = __webpack_require__("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
  511. var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag);
  512. // EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleDomAPI.js
  513. var styleDomAPI = __webpack_require__("./node_modules/style-loader/dist/runtime/styleDomAPI.js");
  514. var styleDomAPI_default = /*#__PURE__*/__webpack_require__.n(styleDomAPI);
  515. // EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertBySelector.js
  516. var insertBySelector = __webpack_require__("./node_modules/style-loader/dist/runtime/insertBySelector.js");
  517. var insertBySelector_default = /*#__PURE__*/__webpack_require__.n(insertBySelector);
  518. // EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js
  519. var setAttributesWithoutAttributes = __webpack_require__("./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js");
  520. var setAttributesWithoutAttributes_default = /*#__PURE__*/__webpack_require__.n(setAttributesWithoutAttributes);
  521. // EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertStyleElement.js
  522. var insertStyleElement = __webpack_require__("./node_modules/style-loader/dist/runtime/insertStyleElement.js");
  523. var insertStyleElement_default = /*#__PURE__*/__webpack_require__.n(insertStyleElement);
  524. // EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleTagTransform.js
  525. var styleTagTransform = __webpack_require__("./node_modules/style-loader/dist/runtime/styleTagTransform.js");
  526. var styleTagTransform_default = /*#__PURE__*/__webpack_require__.n(styleTagTransform);
  527. // EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/style/main.less
  528. var main = __webpack_require__("./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/style/main.less");
  529. ;// CONCATENATED MODULE: ./src/style/main.less
  530.  
  531.  
  532. var options = {};
  533.  
  534. options.styleTagTransform = (styleTagTransform_default());
  535. options.setAttributes = (setAttributesWithoutAttributes_default());
  536. options.insert = insertBySelector_default().bind(null, "head");
  537. options.domAPI = (styleDomAPI_default());
  538. options.insertStyleElement = (insertStyleElement_default());
  539.  
  540. var update = injectStylesIntoStyleTag_default()(main/* default */.A, options);
  541.  
  542.  
  543.  
  544.  
  545. /* harmony default export */ const style_main = (main/* default */.A && main/* default */.A.locals ? main/* default */.A.locals : undefined);
  546.  
  547. ;// CONCATENATED MODULE: ./src/SettingsStorage.ts
  548. class SettingsStorage {
  549. /**
  550. * Initializes a new instance of the SettingsStorage class with the specified storage key.
  551. *
  552. * @param {string} storageKey - The key used to store the settings in the local storage.
  553. */
  554. constructor(storageKey) {
  555. this.storageKey = storageKey;
  556. }
  557. /**
  558. * Saves the given settings to the local storage.
  559. *
  560. * @param {any} settings - The settings to be saved.
  561. * @return {void} This function does not return anything.
  562. */
  563. saveSettings(settings) {
  564. localStorage.setItem(this.storageKey, JSON.stringify(settings));
  565. }
  566. /**
  567. * Loads the settings from the local storage.
  568. *
  569. * @return {any} The loaded settings, or null if no settings are found.
  570. */
  571. loadSettings() {
  572. const settings = localStorage.getItem(this.storageKey);
  573. return settings ? JSON.parse(settings) : null;
  574. }
  575. /**
  576. * Updates a specific setting in the local storage.
  577. *
  578. * @param {string} key - The key of the setting to update.
  579. * @param {any} value - The new value for the setting.
  580. * @return {void} This function does not return anything.
  581. */
  582. updateSetting(key, value) {
  583. const settings = this.loadSettings() || {};
  584. settings[key] = value;
  585. this.saveSettings(settings);
  586. }
  587. /**
  588. * Retrieves a specific setting from the local storage.
  589. *
  590. * @param {string} key - The key of the setting to retrieve.
  591. * @return {any} The value of the setting, or null if the setting is not found.
  592. */
  593. getSetting(key) {
  594. const settings = this.loadSettings();
  595. return settings ? settings[key] : null;
  596. }
  597. /**
  598. * Removes a specific setting from the local storage.
  599. *
  600. * @param {string} key - The key of the setting to remove.
  601. * @return {void} This function does not return anything.
  602. */
  603. removeSetting(key) {
  604. const settings = this.loadSettings();
  605. if (settings && key in settings) {
  606. delete settings[key];
  607. this.saveSettings(settings);
  608. }
  609. }
  610. }
  611. SettingsStorage.instance = new SettingsStorage("WME_wazemySettings");
  612.  
  613. ;// CONCATENATED MODULE: ./src/plugins/PluginTooltip.ts
  614.  
  615.  
  616. class PluginTooltip {
  617. constructor() {
  618. this.initialize();
  619. }
  620. /**
  621. * Initializes the plugin by adding settings into the tab pane, setting the initial state of the settings based on the last stored value, and adding a hidden tooltip window.
  622. *
  623. * @return {void} This function does not return anything.
  624. */
  625. initialize() {
  626. // Add settings into tab pane.
  627. const settingsHTML = `<input type="checkbox" id="wazemySettings_tooltip_enable"/>
  628. <label for="wazemySettings_tooltip_enable">Enable map tooltip</label>`;
  629. $("#wazemySettings_settings").append(settingsHTML);
  630. $("#wazemySettings_tooltip_enable").on("change", () => {
  631. PluginManager.instance.updatePluginSettings("tooltip", {
  632. enable: $("#wazemySettings_tooltip_enable").prop("checked"),
  633. });
  634. });
  635. // Set settings according to last stored value.
  636. const savedSettings = SettingsStorage.instance.getSetting("tooltip");
  637. if (savedSettings?.enable === true) {
  638. $("#wazemySettings_tooltip_enable").prop("checked", true);
  639. }
  640. else {
  641. $("#wazemySettings_tooltip_enable").prop("checked", false);
  642. }
  643. // Add hidden tooltip window.
  644. const tooltipHTML = `<div id="wazemyTooltip"></div>`;
  645. $(document.body).append(tooltipHTML);
  646. console.log("[WazeMY] PluginTooltip initialized.");
  647. }
  648. /**
  649. * Enables the PluginTooltip by registering the "mousemove" event, showing the tooltip, and logging a message.
  650. *
  651. * @return {void} This function does not return anything.
  652. */
  653. enable() {
  654. WazeWrap.Events.register("mousemove", null, this.showTooltip);
  655. $("#wazemyTooltip").show();
  656. console.log("[WazeMY] PluginTooltip enabled.");
  657. }
  658. /**
  659. * Disables the PluginTooltip by unregistering the "mousemove" event, hiding the tooltip, and logging a message.
  660. *
  661. * @return {void} This function does not return anything.
  662. */
  663. disable() {
  664. WazeWrap.Events.unregister("mousemove", null, this.showTooltip);
  665. $("#wazemyTooltip").hide();
  666. console.log("[WazeMY] PluginTooltip disabled.");
  667. }
  668. /**
  669. * Updates the settings of the PluginTooltip based on the provided settings object.
  670. *
  671. * @param {any} settings - The new settings object.
  672. * @return {void} This function does not return anything.
  673. */
  674. updateSettings(settings) {
  675. if (settings.enable === true) {
  676. this.enable();
  677. }
  678. else {
  679. this.disable();
  680. }
  681. console.log("[WazeMY] PluginTooltip settings updated.", settings);
  682. }
  683. /**
  684. * Shows the tooltip at the mouse position.
  685. *
  686. * @param {MouseEvent} e - The mouse event.
  687. * @return {void} This function does not return anything.
  688. */
  689. showTooltip(e) {
  690. let output = "";
  691. let showTooltip = false;
  692. // Manual check of settings because unregistering event is not working.
  693. if ($("#wazemySettings_tooltip_enable").prop("checked") === true) {
  694. const landmark = W.map.venueLayer.getFeatureBy("renderIntent", "highlight");
  695. const segment = W.map.segmentLayer.getFeatureBy("renderIntent", "highlight");
  696. if (landmark) {
  697. output = `<b>${landmark.attributes.wazeFeature._wmeObject.attributes.name}</b><br>`;
  698. const address = landmark.attributes.wazeFeature._wmeObject.getAddress();
  699. try {
  700. output += address.getHouseNumber()
  701. ? `${address.getHouseNumber()}, `
  702. : "";
  703. output += address.getStreetName()
  704. ? `${address.getStreetName()}<br>`
  705. : `No street<br>`;
  706. output += `${address.getCityName()}, `;
  707. output += `${address.getStateName()}<br>`;
  708. }
  709. catch {
  710. output += "No address<br>";
  711. }
  712. output += `<b>Lock:</b> ${landmark.attributes.wazeFeature._wmeObject.getLockRank() + 1}`;
  713. showTooltip = true;
  714. }
  715. else if (segment) {
  716. const segmentId = segment.attributes.wazeFeature.id;
  717. const address = segment.attributes.wazeFeature._wmeObject.getAddress();
  718. output = `<b>${address.getStreetName()}</b><br>`;
  719. const altStreets = address.getAltStreets();
  720. for (let i = 0; i < altStreets.length; i++) {
  721. const altStreetName = altStreets[i].getStreetName();
  722. output += `Alt: ${altStreetName}<br>`;
  723. }
  724. output += `${address.getCityName()}, ${address.getStateName()}<br>`;
  725. output += `<b>ID:</b> ${segmentId}<br>`;
  726. output += `<b>Lock:</b> ${segment.attributes.wazeFeature._wmeObject.getLockRank() + 1}`;
  727. showTooltip = true;
  728. }
  729. const tooltipDiv = $("#wazemyTooltip");
  730. if (showTooltip === true) {
  731. const tw = tooltipDiv.innerWidth();
  732. const th = tooltipDiv.innerHeight();
  733. let tooltipX = e.clientX + window.scrollX + 15;
  734. let tooltipY = e.clientY + window.scrollY + 15;
  735. // Handle cases where tooltip is too near the edge.
  736. if (tooltipX + tw > W.map.$map.innerWidth()) {
  737. tooltipX -= tw + 20; // 20 = scroll bar size
  738. if (tooltipX < 0) {
  739. tooltipX = 0;
  740. }
  741. }
  742. if (tooltipY + th > W.map.$map.innerHeight()) {
  743. tooltipY -= th + 20;
  744. if (tooltipY < 0) {
  745. tooltipY = 0;
  746. }
  747. }
  748. tooltipDiv.html(output);
  749. tooltipDiv.css("top", `${tooltipY}px`);
  750. tooltipDiv.css("left", `${tooltipX}px`);
  751. tooltipDiv.css("visibility", "visible");
  752. }
  753. else {
  754. tooltipDiv.css("visibility", "hidden");
  755. }
  756. }
  757. }
  758. }
  759.  
  760. ;// CONCATENATED MODULE: ./src/plugins/PluginCopyLatLon.ts
  761. class PluginCopyLatLon {
  762. constructor() {
  763. this.initialize();
  764. }
  765. /**
  766. * Initialize plugin.
  767. *
  768. * @return {void} This function does not return anything.
  769. */
  770. initialize() {
  771. const settingsHTML = `<div>Ctrl+Alt+C: <i>Copy lat/lon of mouse position to clipboard.</i></div>`;
  772. $("#wazemySettings_shortcuts").append(settingsHTML);
  773. this.enable(); // Manually enable plugin since there is no settings to trigger this.
  774. console.log("[WazeMY] PluginCopyLatLon initialized.");
  775. }
  776. /**
  777. * Enable plugin.
  778. *
  779. * @return {void} This function does not return anything.
  780. */
  781. enable() {
  782. new WazeWrap.Interface.Shortcut("WazeMY_latloncopy", "Copies lat/lon of mouse position to clipboard.", "wazemy", "WazeMY", "CA+c", this.copyLatLon, null).add();
  783. console.log("[WazeMY] PluginCopyLatLon enabled.");
  784. }
  785. /**
  786. * Disable plugin.
  787. *
  788. * @return {void} This function does not return anything.
  789. */
  790. disable() {
  791. console.log("[WazeMY] PluginCopyLatLon disabled.");
  792. }
  793. /**
  794. * Updates the settings of the PluginCopyLatLon based on the provided settings object.
  795. *
  796. * @return {void} This function does not return anything.
  797. */
  798. updateSettings(settings) {
  799. console.log("[WazeMY] PluginCopyLatLon settings updated.", settings);
  800. }
  801. /**
  802. * Copies lat/lon of mouse position to clipboard.
  803. *
  804. * @return {void} This function does not return anything.
  805. */
  806. copyLatLon() {
  807. const latlon = $(".wz-map-ol-control-span-mouse-position").text();
  808. navigator.clipboard.writeText(latlon);
  809. }
  810. }
  811.  
  812. ;// CONCATENATED MODULE: ./src/plugins/PluginTrafficCameras.ts
  813.  
  814.  
  815. class PluginTrafficCameras {
  816. constructor() {
  817. this.initialize();
  818. }
  819. initialize() {
  820. // Add settings into view.
  821. const settingsHTML = `<div>
  822. <input type="checkbox" id="wazemySettings_trafcam_enable" style="margin-top:0px"/>
  823. <label for="wazemySettings_trafcam_enable">Traffic cameras</label><br></div>`;
  824. const wazemySettings = document.getElementById("wazemySettings_settings");
  825. $("#wazemySettings_settings").append(settingsHTML);
  826. // wazemySettings.insertAdjacentHTML("afterbegin", settingsHTML);
  827. const settingsEl = document.getElementById(`wazemySettings_trafcam_enable`);
  828. const savedSettings = SettingsStorage.instance.getSetting("trafcam");
  829. if (savedSettings?.enable === true) {
  830. settingsEl.checked = true;
  831. }
  832. else {
  833. settingsEl.checked = false;
  834. }
  835. settingsEl.onchange = (e) => {
  836. const target = e.target;
  837. PluginManager.instance.updatePluginSettings("trafcam", {
  838. enable: target.checked,
  839. });
  840. };
  841. // Install camera icon
  842. if (!OpenLayers.Icon) {
  843. this.installIconClass();
  844. }
  845. this.trafcamLayer = new OpenLayers.Layer.Markers("wazemyTrafcamLayer");
  846. W.map.addLayer(this.trafcamLayer);
  847. this.showIcons();
  848. console.log("PluginTrafficCameras initialized.");
  849. }
  850. enable() {
  851. console.log("PluginTrafficCameras enabled.");
  852. this.trafcamLayer.setVisibility(true);
  853. }
  854. disable() {
  855. console.log("PluginTrafficCameras disabled.");
  856. this.trafcamLayer.setVisibility(false);
  857. }
  858. updateSettings(settings) {
  859. if (settings.enable === true) {
  860. this.enable();
  861. }
  862. else {
  863. this.disable();
  864. }
  865. console.log("PluginTrafficCameras settings updated", settings);
  866. }
  867. installIconClass() {
  868. OpenLayers.Icon = OpenLayers.Class({
  869. url: null,
  870. size: null,
  871. offset: null,
  872. calculateOffset: null,
  873. imageDiv: null,
  874. px: null,
  875. initialize: function (url, size, offset, calculateOffset) {
  876. this.url = url;
  877. this.size = size || { w: 20, h: 20 };
  878. this.offset = offset || {
  879. x: -(this.size.w / 2),
  880. y: -(this.size.h / 2),
  881. };
  882. this.calculateOffset = calculateOffset;
  883. url = OpenLayers.Util.createUniqueID("OL_Icon_");
  884. const div = (this.imageDiv = OpenLayers.Util.createAlphaImageDiv(url));
  885. $(div.firstChild).removeClass("olAlphaImg"); // LEAVE THIS LINE TO PREVENT WME-HARDHATS SCRIPT FROM TURNING ALL ICONS INTO HARDHAT WAZERS --MAPOPMATIC
  886. },
  887. destroy: function () {
  888. this.erase();
  889. OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);
  890. this.imageDiv.innerHTML = "";
  891. this.imageDiv = null;
  892. },
  893. clone: function () {
  894. return new OpenLayers.Icon(this.url, this.size, this.offset, this.calculateOffset);
  895. },
  896. setSize: function (size) {
  897. null !== size && (this.size = size);
  898. this.draw();
  899. },
  900. setUrl: function (url) {
  901. null !== url && (this.url = url);
  902. this.draw();
  903. },
  904. draw: function (a) {
  905. OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, this.size, this.url, "absolute");
  906. this.moveTo(a);
  907. return this.imageDiv;
  908. },
  909. erase: function () {
  910. null !== this.imageDiv &&
  911. null !== this.imageDiv.parentNode &&
  912. OpenLayers.Element.remove(this.imageDiv);
  913. },
  914. setOpacity: function (a) {
  915. OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, null, null, null, null, a);
  916. },
  917. moveTo: function (a) {
  918. null !== a && (this.px = a);
  919. null !== this.imageDiv &&
  920. (null === this.px
  921. ? this.display(!1)
  922. : (this.calculateOffset &&
  923. (this.offset = this.calculateOffset(this.size)),
  924. OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
  925. x: this.px.x + this.offset.x,
  926. y: this.px.y + this.offset.y,
  927. })));
  928. },
  929. display: function (a) {
  930. this.imageDiv.style.display = a ? "" : "none";
  931. },
  932. isDrawn: function () {
  933. return (this.imageDiv &&
  934. this.imageDiv.parentNode &&
  935. 11 != this.imageDiv.parentNode.nodeType);
  936. },
  937. CLASS_NAME: "OpenLayers.Icon",
  938. });
  939. }
  940. showIcons() {
  941. trafficCamsData.forEach((e, idx) => {
  942. this.drawCamIcon({
  943. idx: idx,
  944. desc: e.desc,
  945. src: e.url,
  946. width: 20,
  947. height: 20,
  948. lat: e.lat,
  949. lon: e.lon,
  950. });
  951. });
  952. }
  953. drawCamIcon(spec) {
  954. const camIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAAGXcA1uAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpBRDNGNTkwRTYzQThFMzExQTc4MDhDNjAwODdEMzdEQSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2OUI0RUEyN0IwRjcxMUUzOERFM0E1OTJCRUY3NTFBOCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2OUI0RUEyNkIwRjcxMUUzOERFM0E1OTJCRUY3NTFBOCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZGOEJBMzExNkZCMEUzMTFCOEY5QTU3QUQxM0M2MjI5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkFEM0Y1OTBFNjNBOEUzMTFBNzgwOEM2MDA4N0QzN0RBIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+TV0cjwAABbhJREFUeNpiYIAAXiZGCIMPiP//f8DwnwnI+PT/HZD8y8AAEEBQVQzTwOSfuwz/u6qAyh4y/Gf8f4/hPwMnUJSZgQEggMCyzpZAmbsILMTHcAokPhPEWT8dqJoBgvetAtMMDBIiDCf/P4bqeMXwf+t8BiOAAGJAAqVA7A1iPH/+goFBT4OB8f9LJDueAzFQgOHGLoTZYEGgpJgww0+G/68ZQArAEsryEHpRN5BuK4FIcHMhjJORVTvBYG3MwPD2CkKwKAVI/3nBABBAYOcY6zKAjGSQEmP4cGkXxMlgK4BeaCll+B/lzzDh/yegmr8vIO4HGv/l/0eG/w4WDP9fnUM4Dhl/uMzwf/6CFQ4g9RUgE3ctRvgGGSvJMfw/tw3i7sY8hv8sQMGrQE8yuFoBZe8CeRwMDIKaDAyWRgwM2xYD+exA/AuIvwAV3kaE6sSPQCuBHv2vqczwf0YL0MR7SE4CsgsTGP5///aSCZQSGDRVGPJfv2dgNDNkcP30heHbB6BpyzYyMCRVMjCwqDIcOX+LgTEtioGRhfn/P4AAbJTfK0NhGMe/Z+ecpVkrScnIlCiWkoulpFyhxgXKXCGK3XGBG/4BJcoNN665tqsxo6XshiKtyQybn82vMZPF63nPOXKYi+fieU7P8z59P9/n6NlBVNrRRDFPMUlRIGhmM0gWzk5NSCFMuDE2MqAaUJGUhDgOgNmKkXmLwMRudA11diynI9lSKhEHG+oBu9q1mHkDf9AWWkM0dgHEb4H+PqqkKD51uxpJuSoDHpIfAowyohyUveJH+9pqUjigav/9UnIfzO/frkRvBxUuwcpK/gc3PkzfzynuwRoanYuStZDKaeAkwA8QEPJ/CYfpBUCmqxp1E7uXJ6u0tUNVQbvIR+DIBzgHgQMvrZ5LZWIiSuowh6N+nQ9ZYgnNcLTzdRBsz5Ot1socWCr1KipYulrJVDIQjqjwgqsESvcPQB5QWmP2nsWem5X80IeizhaadPfHQwTxnXJTDk5ZQgeOOCC0ScY0wtPdRrc4AzY7BVZuQ8bVDhcXJLyhNnwJUFj5hTQVhmH8mQ7H5nYYkRxJw8hqBWYsLIr+gisKYobdjKguClOKLiQvgrrwqogiIr1wECHdRCCjIopNiCKZIGkthysrrWSklg36M6O5fT3fzmk7C8kDL4zt2/neP8/ze01/b6XuceWsVmAJJR56gurObjSn0/DWrEY152SmIyFBNk1sxZhBZAQTyVmEDjeiy9eAQdm4FK1gLlHg8Y3CYlXzZW12A1+GYd1Yi1u1LogXQSYgmxdnjM8jsTFNZvLMxADE3h0QS8vxNNqLJWKa2ZMXBRXwaaNmL/XdoUct+hhtjF+MFBZ+zJq5Gw6ywoRyI/xs/JjJtCj38+XWo3rGdM6pI3nlqYshmmiGx7fJpLE8rAqGZQy+49o5CNeauoeplJZZPbWfztqSWurvmV/ixrCXQjRSFQE/xI8R/dK44VJae99OorWt/Xh2tZxp0Q+903zynX/q6YLwevgy28IXyiBYxCY3cXYeIvGGRL0Isc697Z5k0NRMQj8Grd92zuDALuAuIRm8CVSohe1uYZ+T74FwADjdBFBh6GgH+mm3ZuLDSb9Ocg9YLNYZOeSyUitevupFeWWVTlzjQ0dNfQJW1fOybulPWlmyZ85wpshQC43/m+9YsR2ZTn9wg5z955+z2FO3H0PRIIqcHHzgPpAgNukwOJzAwCB3NiFQxsyyjJ37J4lMXklpfnaRyidaO3xe7+6h3JmVy2CjkcInD3EO3/T1xsFn3mobX0z+RzkfNAxcpXrIjo+RkFKp0VLkk1hfwwqJr69RODxbcH05gem/wIEN62TV13XuMxNIvqYYqCT6R6x14UHsEarkwhBxJbtnC4wmS9fXDuT2stFkxIDsp4tfbZU5MCr0jnMbIMLoKy7Gc8WunU3rxHMoCmKxUaiqij/5alOWhMPoGAAAAABJRU5ErkJggg==";
  955. const size = new OpenLayers.Size(20, 20);
  956. const icon = new OpenLayers.Icon(camIcon, size);
  957. const epsg4326 = new OpenLayers.Projection("EPSG:4326"); // WGS 1984 projection. Malaysia uses EPSG:900913
  958. const projectTo = W.map.getProjectionObject();
  959. const lonLat = new OpenLayers.LonLat(spec.lon, spec.lat).transform(epsg4326, projectTo);
  960. const newMarker = new OpenLayers.Marker(lonLat, icon);
  961. newMarker.idx = spec.idx;
  962. newMarker.title = spec.desc;
  963. newMarker.url = spec.src;
  964. newMarker.width = spec.width;
  965. newMarker.height = spec.height;
  966. newMarker.location = lonLat;
  967. newMarker.events.register("click", newMarker, this.popupCam);
  968. this.trafcamLayer.addMarker(newMarker);
  969. }
  970. popupCam(e) {
  971. popupCam_close(); // Close existing popup if already opened.
  972. var popupHTML = `<div id="gmPopupContainerCam" style="margin:1;text-align:center;padding:5px;z-index:1100;position:absolute;color:white;background:rgba(0,0,0,0.5)">
  973. <table border=0>
  974. <tr>
  975. <td><div id="mycamdivheader" style="min-height:20px;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;width:380px">${e.object.title}</div></td>
  976. <td align="right"><a href="#close" id="gmCloseCamDlgBtn" title="Close" style="color:red">X</a></td>
  977. </tr>
  978. <tr><td colspan=2>Select source:
  979. <select id="wazemy_camSource">
  980. </select>
  981. <div hidden id="mycamid">${e.object.idx}</div>
  982. </td></tr>
  983. <tr><td colspan=2><img style="width:400px" id="staticimage"></td></tr>
  984. <tr><td colspan=2><div id="mycamstatus"></div></td></tr>
  985. </table></div>`;
  986. document.body.insertAdjacentHTML("afterbegin", popupHTML);
  987. // Handle cases where popup is too near the edge.
  988. let tw = $("#gmPopupContainerCam").width();
  989. let th = $("#gmPopupContainerCam").height() + 200;
  990. var tooltipX = e.clientX + window.scrollX + 15;
  991. var tooltipY = e.clientY + window.scrollY + 15;
  992. if (tooltipX + tw > W.map.$map.innerWidth()) {
  993. tooltipX -= tw + 20; // 20 = scroll bar size
  994. if (tooltipX < 0)
  995. tooltipX = 0;
  996. }
  997. if (tooltipY + th > W.map.$map.innerHeight()) {
  998. tooltipY -= th + 20;
  999. if (tooltipY < 0)
  1000. tooltipY = 0;
  1001. }
  1002. $("#gmPopupContainerCam").css({ left: tooltipX });
  1003. $("#gmPopupContainerCam").css({ top: tooltipY });
  1004. //Add listener for popup's "Close" button
  1005. const closeBtn = document.getElementById("gmCloseCamDlgBtn");
  1006. closeBtn.onclick = popupCam_close;
  1007. // Allow popup to be draggable.
  1008. const popupContainerEl = document.getElementById("gmPopupContainerCam");
  1009. popup_dragElement(popupContainerEl);
  1010. const camSourceEl = document.getElementById("wazemy_camSource");
  1011. for (let urlsrc in e.object.url) {
  1012. if (urlsrc === "LLM" && e.object.url["LLM"].split("|").length == 2) {
  1013. popup_appendOption(urlsrc);
  1014. }
  1015. else if (urlsrc === "Jalanow") {
  1016. popup_appendOption(urlsrc);
  1017. }
  1018. }
  1019. camSourceEl.onchange = (e) => {
  1020. console.log("PluginTrafficCameras: Camera source selection changed.");
  1021. const camId = document.getElementById("mycamid");
  1022. const target = e.target;
  1023. switch (target.selectedOptions[0].innerText) {
  1024. case "Jalanow":
  1025. popup_getJalanowImage(trafficCamsData[camId.innerText]["url"]["Jalanow"]);
  1026. break;
  1027. case "LLM":
  1028. popup_getLLMImage(trafficCamsData[camId.innerText]["url"]["LLM"]);
  1029. break;
  1030. }
  1031. };
  1032. // Get image for the first time when popup is displayed.
  1033. switch (Object.keys(e.object.url)[0]) {
  1034. case "Jalanow":
  1035. popup_getJalanowImage(e.object.url["Jalanow"]);
  1036. break;
  1037. case "LLM":
  1038. popup_getLLMImage(e.object.url["LLM"]);
  1039. break;
  1040. }
  1041. function popupCam_close() {
  1042. const popupContainerEl = document.getElementById("gmPopupContainerCam");
  1043. if (popupContainerEl) {
  1044. popupContainerEl.remove();
  1045. popupContainerEl.hidden = true;
  1046. }
  1047. }
  1048. function popup_dragElement(elmnt) {
  1049. var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  1050. if (document.getElementById("mycamdivheader")) {
  1051. // if present, the header is where you move the DIV from:
  1052. document.getElementById("mycamdivheader").onmousedown = dragMouseDown;
  1053. }
  1054. else {
  1055. // otherwise, move the DIV from anywhere inside the DIV:
  1056. elmnt.onmousedown = dragMouseDown;
  1057. }
  1058. function dragMouseDown(e) {
  1059. e.preventDefault();
  1060. // get the mouse cursor position at startup:
  1061. pos3 = e.clientX;
  1062. pos4 = e.clientY;
  1063. document.onmouseup = closeDragElement;
  1064. // call a function whenever the cursor moves:
  1065. document.onmousemove = elementDrag;
  1066. }
  1067. function elementDrag(e) {
  1068. e.preventDefault();
  1069. // calculate the new cursor position:
  1070. pos1 = pos3 - e.clientX;
  1071. pos2 = pos4 - e.clientY;
  1072. pos3 = e.clientX;
  1073. pos4 = e.clientY;
  1074. // set the element's new position:
  1075. const popupContainerEl = document.getElementById("gmPopupContainerCam");
  1076. popupContainerEl.style.top = popupContainerEl.offsetTop - pos2 + "px";
  1077. popupContainerEl.style.left = popupContainerEl.offsetLeft - pos1 + "px";
  1078. }
  1079. function closeDragElement() {
  1080. // stop moving when mouse button is released:
  1081. document.onmouseup = null;
  1082. document.onmousemove = null;
  1083. }
  1084. }
  1085. function popup_appendOption(urlsrc) {
  1086. const option = document.createElement("option");
  1087. option.value = urlsrc;
  1088. option.text = urlsrc;
  1089. camSourceEl.append(option);
  1090. }
  1091. function popup_getJalanowImage(url) {
  1092. GM_xmlhttpRequest({
  1093. method: "GET",
  1094. responseType: "blob",
  1095. headers: {
  1096. authority: "p4.fgies.com",
  1097. referer: "https://www.jalanow.com/",
  1098. accept: "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
  1099. },
  1100. url: url,
  1101. onload: function (response) {
  1102. const staticImageEl = document.getElementById("staticimage");
  1103. staticImageEl.src = URL.createObjectURL(response.response);
  1104. document.getElementById("mycamstatus").innerHTML = "";
  1105. },
  1106. onerror: function (response) {
  1107. document.getElementById("mycamstatus").innerHTML =
  1108. "Error loading image.";
  1109. },
  1110. onprogress: function (response) {
  1111. document.getElementById("mycamstatus").innerHTML = "Loading image...";
  1112. },
  1113. });
  1114. }
  1115. function popup_getLLMImage(url) {
  1116. let camImg = url.split("|");
  1117. GM_xmlhttpRequest({
  1118. method: "GET",
  1119. responseType: "blob",
  1120. url: camImg[0],
  1121. onload: function (response) {
  1122. const re = new RegExp('src="data:image/png;base64, ([A-Za-z0-9/+=]*)" title="' +
  1123. camImg[1] +
  1124. '"');
  1125. const m = response.responseText.match(re);
  1126. const staticImageEl = document.getElementById("staticimage");
  1127. staticImageEl.src = "data:image/png;base64," + m[1];
  1128. document.getElementById("mycamstatus").innerHTML = "";
  1129. },
  1130. onerror: function (response) {
  1131. document.getElementById("mycamstatus").innerHTML =
  1132. "Error loading image.";
  1133. },
  1134. onprogress: function (response) {
  1135. document.getElementById("mycamstatus").innerHTML = "Loading image...";
  1136. },
  1137. });
  1138. }
  1139. }
  1140. }
  1141.  
  1142. ;// CONCATENATED MODULE: ./src/plugins/PluginKVMR.ts
  1143.  
  1144.  
  1145. class PluginKVMR {
  1146. constructor() {
  1147. this.areas = [
  1148. {
  1149. name: "Area 1",
  1150. geometry: "POLYGON ((101.296968 3.210365, 101.30376 3.118812, 101.327352 3.100621, 101.332475 3.083048, 101.3447571 3.0671528, 101.3475037 2.9992702, 101.4546204 2.9862417, 101.443634 3.2097608, 101.296968 3.210365))",
  1151. color: "#ffffff",
  1152. },
  1153. {
  1154. name: "Area 2a",
  1155. geometry: "POLYGON ((101.443634 3.2097608, 101.4546204 2.9862417, 101.542797963266 2.999451622765, 101.546214771469 3.20939581928522, 101.443634 3.2097608))",
  1156. color: "#ff0000",
  1157. },
  1158. {
  1159. name: "Area 2b",
  1160. geometry: "POLYGON ((101.546208402411 3.20940523257651, 101.545212203573 3.14955753765693, 101.640751782444 3.14826179285013, 101.6379547 3.2090753, 101.546208402411 3.20940523257651))",
  1161. color: "#00ff00",
  1162. },
  1163. {
  1164. name: "Area 2c",
  1165. geometry: "POLYGON ((101.545226515908 3.14956187716133, 101.544049591624 3.0763653393285, 101.643784992874 3.08223941174546, 101.640750558754 3.14825692296985, 101.545226515908 3.14956187716133))",
  1166. color: "#0000ff",
  1167. },
  1168. {
  1169. name: "Area 2d",
  1170. geometry: "POLYGON ((101.544050641663 3.07636531606873, 101.542795175242 2.99943779058738, 101.6468811 3.0150413, 101.643784373198 3.08223705403019, 101.544050641663 3.07636531606873))",
  1171. color: "#ffff00",
  1172. },
  1173. {
  1174. name: "Area 3",
  1175. geometry: "POLYGON ((101.6379547 3.2090753, 101.6412146 3.145488, 101.7443848 3.1501147, 101.7309414 3.2090495, 101.6379547 3.2090753))",
  1176. color: "#ff00ff",
  1177. },
  1178. {
  1179. name: "Area 4",
  1180. geometry: "POLYGON ((101.6412146 3.145488, 101.6439612 3.0796673, 101.7615509 3.0791519, 101.7443848 3.1501147, 101.6412146 3.145488))",
  1181. color: "#00ffff",
  1182. },
  1183. {
  1184. name: "Area 5",
  1185. geometry: "POLYGON ((101.788224 3.210399, 101.7309414 3.2090495, 101.7615509 3.0791519, 101.6439612 3.0796673, 101.6468811 3.0150413, 101.8391418 3.0253266, 101.788224 3.210399))",
  1186. color: "#40ff00",
  1187. },
  1188. {
  1189. name: "Area 6",
  1190. geometry: "POLYGON ((101.332475 3.083048, 101.262098 3.074553, 101.2335205 3.0815516, 101.208976 3.060844, 101.162394 2.989639, 101.223721 2.903504, 101.268598 2.871386, 101.284902 2.830662, 101.448138 2.72835, 101.4546204 2.9862417, 101.3475037 2.9992702, 101.3447571 3.0671528, 101.332475 3.083048))",
  1191. color: "#ff4000",
  1192. },
  1193. {
  1194. name: "Area 7",
  1195. geometry: "POLYGON ((101.4546204 2.9862417, 101.448138 2.72835, 101.477474 2.766274, 101.559411 2.807463, 101.6578674 2.8223442, 101.6468811 3.0150413, 101.4546204 2.9862417))",
  1196. color: "#33ff00",
  1197. },
  1198. {
  1199. name: "Area 8",
  1200. geometry: "POLYGON ((101.6578674 2.8223442, 101.725067 2.83344, 101.756459 2.866068, 101.882828 2.870563, 101.8391418 3.0253266, 101.6468811 3.0150413, 101.6578674 2.8223442))",
  1201. color: "#ff0033",
  1202. },
  1203. ];
  1204. this.initialize();
  1205. }
  1206. initialize() {
  1207. // Add settings into view.
  1208. const settingsHTML = `<div>
  1209. <input type="checkbox" id="wazemySettings_kvmr_enable" style="margin-top:0px"/>
  1210. <label for="wazemySettings_kvmr_enable">Klang Valley Map Raid</label><br></div>`;
  1211. $("#wazemySettings_settings").append(settingsHTML);
  1212. const settingsEl = document.getElementById(`wazemySettings_kvmr_enable`);
  1213. const savedSettings = SettingsStorage.instance.getSetting("kvmr");
  1214. if (savedSettings?.enable === true) {
  1215. settingsEl.checked = true;
  1216. }
  1217. else {
  1218. settingsEl.checked = false;
  1219. }
  1220. settingsEl.onchange = (e) => {
  1221. const target = e.target;
  1222. PluginManager.instance.updatePluginSettings("kvmr", {
  1223. enable: target.checked,
  1224. });
  1225. };
  1226. // Add MR polygon overlay.
  1227. const mro_Map = W.map;
  1228. const mro_OL = OpenLayers;
  1229. // const mro_mapLayers = mro_Map.getLayersBy("uniqueName", "__KlangValley");
  1230. this.raid_mapLayer = new mro_OL.Layer.Vector("KlangValley", {
  1231. displayInLayerSwitcher: true,
  1232. uniqueName: "__KlangValley",
  1233. });
  1234. mro_Map.addLayer(this.raid_mapLayer);
  1235. this.areas.forEach((area) => {
  1236. const geometry = parseWKT(area.geometry);
  1237. this.addRaidPolygon(this.raid_mapLayer, geometry, area.color, area.name);
  1238. });
  1239. mro_Map.events.register("moveend", W.map, function () {
  1240. currentRaidLocation();
  1241. });
  1242. mro_Map.events.register("zoomend", W.map, function () {
  1243. currentRaidLocation();
  1244. });
  1245. console.log("PluginKVMR initialized.");
  1246. /**
  1247. * Updates the current raid location on the map based on the user's current location.
  1248. *
  1249. * @return {void} This function does not return anything.
  1250. */
  1251. function currentRaidLocation() {
  1252. // Only run if the plugin is enabled. Workaround because unregistering events doesn't work.
  1253. if ($("#wazemySettings_kvmr_enable").is(":checked") === false) {
  1254. return;
  1255. }
  1256. var mro_Map = W.map;
  1257. const mro_mapLayers = mro_Map.getLayersBy("uniqueName", "__KlangValley")[0];
  1258. for (let i = 0; i < mro_mapLayers.features?.length; i++) {
  1259. var raidMapCenter = mro_Map.getCenter();
  1260. var raidCenterPoint = new OpenLayers.Geometry.Point(raidMapCenter.lon, raidMapCenter.lat);
  1261. const raid_mapLayer = mro_Map.getLayersBy("uniqueName", "__KlangValley")[0];
  1262. var raidCenterCheck = raid_mapLayer.features[i].geometry.components[0].containsPoint(raidCenterPoint);
  1263. var holes = raid_mapLayer.features[i].attributes.holes;
  1264. if (raidCenterCheck === true) {
  1265. var str = $("#topbar-container > div > div.location-info-region > div").text();
  1266. const location = str.split(" - ");
  1267. if (location.length > 1) {
  1268. location[1] =
  1269. "Klang Valley MapRaid " +
  1270. raid_mapLayer.features[i].attributes.number;
  1271. }
  1272. else {
  1273. location.push("Klang Valley MapRaid " +
  1274. raid_mapLayer.features[i].attributes.number);
  1275. }
  1276. const raidLocationLabel = location.join(" - ");
  1277. setTimeout(function () {
  1278. $("#topbar-container > div > div.location-info-region > div").text(raidLocationLabel);
  1279. }, 200);
  1280. if (holes === "false") {
  1281. break;
  1282. }
  1283. }
  1284. }
  1285. }
  1286. function parseWKT(wkt) {
  1287. let trimmed;
  1288. if (wkt.startsWith("POLYGON")) {
  1289. trimmed = wkt.replace("POLYGON ((", "").replace("))", "");
  1290. }
  1291. const coordinatePairs = trimmed.split(", ");
  1292. const coordinates = coordinatePairs.map((pair) => {
  1293. const [lon, lat] = pair.split(" ");
  1294. return { lon, lat };
  1295. });
  1296. return coordinates;
  1297. }
  1298. }
  1299. enable() {
  1300. this.raid_mapLayer.setVisibility(true);
  1301. console.log("PluginKVMR enabled.");
  1302. }
  1303. disable() {
  1304. this.raid_mapLayer.setVisibility(false);
  1305. const mro_map = W.map;
  1306. mro_map.events.unregister("moveend", W.map);
  1307. mro_map.events.unregister("zoomend", W.map);
  1308. console.log("PluginKVMR disabled.");
  1309. }
  1310. updateSettings(settings) {
  1311. if (settings.enable === true) {
  1312. this.enable();
  1313. }
  1314. else {
  1315. this.disable();
  1316. }
  1317. console.log("PluginKVMR settings updated", settings);
  1318. }
  1319. addRaidPolygon(raidLayer, groupPoints, groupColor, groupNumber) {
  1320. var mro_Map = W.map;
  1321. var mro_OL = OpenLayers;
  1322. var raidGroupLabel = "KlangValley " + groupNumber;
  1323. var groupName = "RaidGroup " + groupNumber;
  1324. var style = {
  1325. strokeColor: groupColor,
  1326. strokeOpacity: 0.8,
  1327. strokeWidth: 3,
  1328. fillColor: groupColor,
  1329. fillOpacity: 0.15,
  1330. label: raidGroupLabel,
  1331. labelOutlineColor: "black",
  1332. labelOutlineWidth: 3,
  1333. fontSize: 14,
  1334. fontColor: groupColor,
  1335. fontOpacity: 0.85,
  1336. fontWeight: "bold",
  1337. };
  1338. var attributes = {
  1339. name: groupName,
  1340. number: groupNumber,
  1341. };
  1342. var pnt = [];
  1343. for (let i = 0; i < groupPoints.length; i++) {
  1344. const convPoint = new OpenLayers.Geometry.Point(groupPoints[i].lon, groupPoints[i].lat).transform(new OpenLayers.Projection("EPSG:4326"), mro_Map.getProjectionObject());
  1345. //console.log('MapRaid: ' + JSON.stringify(groupPoints[i]) + ', ' + groupPoints[i].lon + ', ' + groupPoints[i].lat);
  1346. pnt.push(convPoint);
  1347. }
  1348. var ring = new mro_OL.Geometry.LinearRing(pnt);
  1349. var polygon = new mro_OL.Geometry.Polygon([ring]);
  1350. var feature = new mro_OL.Feature.Vector(polygon, attributes, style);
  1351. raidLayer.addFeatures([feature]);
  1352. }
  1353. }
  1354.  
  1355. ;// CONCATENATED MODULE: ./src/plugins/PluginZoomPic.ts
  1356. class PluginZoomPic {
  1357. constructor() {
  1358. this.initialize();
  1359. }
  1360. /**
  1361. * Initialize plugin.
  1362. *
  1363. * @return {void} This function does not return anything.
  1364. */
  1365. initialize() {
  1366. $(document.body).on("click", () => {
  1367. const img = $(".venue-image-dialog > wz-dialog-content > img");
  1368. if (img.length > 0) {
  1369. const newImg = img[0];
  1370. const links = $(".venue-image-dialog > wz-dialog-header > a");
  1371. for (let i = 0; i < links.length; i++) {
  1372. links[i].remove();
  1373. }
  1374. const newImgHTML = `<a href="${newImg.src.replace("thumbs/thumb700_", "")}" target="_blank">(+)</a>`;
  1375. $("wz-dialog-header").append(newImgHTML);
  1376. }
  1377. // $("div.modal-dialog.venue-image-dialog");
  1378. });
  1379. console.log("[WazeMY] PluginZoomPic initialized.");
  1380. }
  1381. /**
  1382. * Enable plugin.
  1383. *
  1384. * @return {void} This function does not return anything.
  1385. */
  1386. enable() {
  1387. console.log("[WazeMY] PluginZoomPic enabled.");
  1388. }
  1389. /**
  1390. * Disable plugin.
  1391. *
  1392. * @return {void} This function does not return anything.
  1393. */
  1394. disable() {
  1395. console.log("[WazeMY] PluginZoomPic disabled.");
  1396. }
  1397. /**
  1398. * Updates the settings of the PluginZoomPic based on the provided settings object.
  1399. *
  1400. * @return {void} This function does not return anything.
  1401. */
  1402. updateSettings(settings) {
  1403. console.log("[WazeMY] PluginZoomPic settings updated.", settings);
  1404. }
  1405. }
  1406.  
  1407. ;// CONCATENATED MODULE: ./src/plugins/PluginPlaces.ts
  1408.  
  1409.  
  1410. class PluginPlaces {
  1411. constructor() {
  1412. this.tabHTML = `
  1413. <div><h4>WazeMY Places</h4></div>
  1414. <div id="wazemyPlaces">
  1415. <select name="wazemyPlaces_polygons" id="wazemyPlaces_polygons"></select>
  1416. <button id="wazemyPlaces_scan">Scan</button>
  1417. <div id="wazemyPlaces_scanStatus"></div>
  1418. <div id="wazemyPlaces_purCount"></div>
  1419. <div id="wazemyPlaces_totalCount"></div>
  1420. <div id="wazemyPlaces_table">
  1421. <table id="wazemyPlaces_venues">
  1422. <thead>
  1423. <tr>
  1424. <th title="I=Image\nN=New Place\nU=Update\nF=Flag\nD=Delete">PUR</th>
  1425. <th>L</th>
  1426. <th>Name</th>
  1427. <th>Errors</th>
  1428. </tr>
  1429. </thead>
  1430. <tbody></tbody>
  1431. </table>
  1432. </div>
  1433. </div>
  1434. `;
  1435. this.initialize();
  1436. }
  1437. /**
  1438. * Initialize plugin.
  1439. *
  1440. * @return {void} This function does not return anything.
  1441. */
  1442. initialize() {
  1443. const settingsHTML = `<input type="checkbox" id="wazemySettings_places_enable"/>
  1444. <label for="wazemySettings_places_enable">Enable Places</label>`;
  1445. $("#wazemySettings_settings").append(settingsHTML);
  1446. $("#wazemySettings_places_enable").on("change", () => {
  1447. PluginManager.instance.updatePluginSettings("places", {
  1448. enable: $("#wazemySettings_places_enable").prop("checked"),
  1449. });
  1450. });
  1451. // Set settings according to last stored value.
  1452. const savedSettings = SettingsStorage.instance.getSetting("places");
  1453. if (savedSettings?.enable === true) {
  1454. $("#wazemySettings_places_enable").prop("checked", true);
  1455. }
  1456. else {
  1457. $("#wazemySettings_places_enable").prop("checked", false);
  1458. }
  1459. console.log("[WazeMY] PluginPlaces initialized.");
  1460. }
  1461. /**
  1462. * Enable plugin.
  1463. *
  1464. * @return {void} This function does not return anything.
  1465. */
  1466. enable() {
  1467. const { tabLabel, tabPane } = W.userscripts.registerSidebarTab("wazemyplaces");
  1468. tabLabel.innerHTML = "WazeMY Places";
  1469. tabLabel.title = "WazeMY Places";
  1470. tabPane.innerHTML = this.tabHTML;
  1471. // Populate select options with polygons from KVMR.
  1472. const map = W.map.getLayersBy("uniqueName", "__KlangValley");
  1473. map[0].features.forEach((feature) => {
  1474. $("#wazemyPlaces_polygons").append($("<option>", {
  1475. value: feature.data.number,
  1476. text: feature.data.number,
  1477. }));
  1478. });
  1479. // Handle Scan button.
  1480. $("#wazemyPlaces_scan").on("click", async () => {
  1481. $("#wazemyPlaces_scanStatus").text("Scanning tiles.");
  1482. $("#wazemyPlaces_venues > tbody").empty();
  1483. const map = W.map.getLayersBy("uniqueName", "__KlangValley");
  1484. if (map.length === 0) {
  1485. console.log("[PluginPlaces] No KVMR layer found. Aborting scan.");
  1486. return false;
  1487. }
  1488. const mr = map[0].getFeaturesByAttribute("number", $("#wazemyPlaces_polygons option:selected")[0].innerText);
  1489. if (mr.length === 0) {
  1490. console.log("[PluginPlaces] No polygon found. Aborting scan.");
  1491. return false;
  1492. }
  1493. const feature = mr[0];
  1494. let bounds = feature.geometry.getBounds().clone();
  1495. bounds = bounds.transform(W.map.getProjectionObject(), "EPSG:4326");
  1496. const venues = await getAllVenues(bounds);
  1497. let purCount = 0;
  1498. let totalCount = 0;
  1499. venues.forEach((venue) => {
  1500. // Check venue against rules.
  1501. const status = evaluateVenue(venue);
  1502. const isPUR = checkPURstatus(venue);
  1503. if (status.priority > 0 || isPUR) {
  1504. // Add venue to table.
  1505. let lon = 0;
  1506. let lat = 0;
  1507. if (venue.geometry.type === "Polygon") {
  1508. lon = venue.geometry.coordinates[0][0][0];
  1509. lat = venue.geometry.coordinates[0][0][1];
  1510. }
  1511. else {
  1512. lon = venue.geometry.coordinates[0];
  1513. lat = venue.geometry.coordinates[1];
  1514. }
  1515. const row = $("<tr>");
  1516. row.attr("id", `${lon}:${lat}:${venue.id}`);
  1517. row.on("click", (e) => {
  1518. const target = e.currentTarget.id.split(":"); // split to lon:lat:id
  1519. const xy = OpenLayers.Layer.SphericalMercator.forwardMercator(parseFloat(target[0]), parseFloat(target[1]));
  1520. W.map.setCenter(xy);
  1521. });
  1522. let purHTML = ``;
  1523. if (isPUR) {
  1524. purCount++;
  1525. if (venue.approved === false) {
  1526. purHTML = `<td align="center">N</td>`;
  1527. }
  1528. else if (venue.venueUpdateRequests[0].type === "REQUEST") {
  1529. if (venue.venueUpdateRequests[0].subType === "FLAG") {
  1530. purHTML = `<td align="center">F</td>`;
  1531. }
  1532. else if (venue.venueUpdateRequests[0].subType === "UPDATE") {
  1533. purHTML = `<td align="center">U</td>`;
  1534. }
  1535. else if (venue.venueUpdateRequests[0].subType === "DELETE") {
  1536. purHTML = `<td align="center">D</td>`;
  1537. }
  1538. else {
  1539. purHTML = `<td align="center">+</td>`;
  1540. }
  1541. }
  1542. else if (venue.venueUpdateRequests[0].type === "IMAGE") {
  1543. purHTML = `<td align="center">I</td>`;
  1544. }
  1545. else {
  1546. purHTML = `<td align="center">+</td>`;
  1547. }
  1548. }
  1549. else {
  1550. purHTML = `<td></td>`;
  1551. }
  1552. row.append(purHTML);
  1553. const levelHTML = `<td>${venue.lockRank ? venue.lockRank + 1 : 1}</td>`;
  1554. row.append(levelHTML);
  1555. const colHTML = `<td>${venue.name}</td>`;
  1556. row.append(colHTML);
  1557. const errorsHTML = `<td>${status.errors.join("\r\n")}</td>`;
  1558. row.append(errorsHTML);
  1559. $("#wazemyPlaces_venues > tbody").append(row);
  1560. totalCount++;
  1561. }
  1562. $("#wazemyPlaces_purCount").text(`# PUR = ${purCount}`);
  1563. $("#wazemyPlaces_totalCount").text(`# total = ${totalCount}`);
  1564. $("#wazemyPlaces_scanStatus").text("");
  1565. function evaluateVenue(venue) {
  1566. let status = {
  1567. priority: 0,
  1568. errors: [],
  1569. };
  1570. // Rule #1
  1571. if (typeof venue.name == "undefined") {
  1572. if (!venue.categories.includes("RESIDENCE_HOME")) {
  1573. status.priority = 3;
  1574. status.errors.push("Missing name.");
  1575. }
  1576. }
  1577. else {
  1578. // Rule: Check name for all uppercase.
  1579. if (venue.name === venue.name.toUpperCase()) {
  1580. status.priority = 3;
  1581. status.errors.push("Name is uppercase.");
  1582. }
  1583. // Rule: Check name for all lowercase.
  1584. if (venue.name === venue.name.toLowerCase()) {
  1585. status.priority = 3;
  1586. status.errors.push("Name is lowercase.");
  1587. }
  1588. }
  1589. // Rule: Min lock is not set.
  1590. if (venue.lockRank === 0) {
  1591. status.priority = 3;
  1592. status.errors.push("Min lock not set.");
  1593. }
  1594. // Rule: Phone number format.
  1595. if (venue.phone) {
  1596. if (/^[\d]{3}-[\d]{3} [\d]{4}$/.test(venue.phone) === false &&
  1597. /^[\d]{3}-[\d]{4} [\d]{4}$/.test(venue.phone) === false &&
  1598. /^[\d]{2}-[\d]{4} [\d]{4}$/.test(venue.phone) === false &&
  1599. /^[\d]{2}-[\d]{3} [\d]{4}$/.test(venue.phone) === false &&
  1600. /^[\d]{3}-[\d]{3} [\d]{3}$/.test(venue.phone) === false &&
  1601. /^[\d]{1}-[\d]{3}-[\d]{2}-[\d]{4}$/.test(venue.phone) === false) {
  1602. status.priority = 2;
  1603. status.errors.push("Phone number format incorrect.");
  1604. }
  1605. }
  1606. // Rule: Category specific rank locks.
  1607. if ((venue.categories.includes("CHARGING_STATION") &&
  1608. venue.lockRank < 3) ||
  1609. (venue.categories.includes("GAS_STATION") && venue.lockRank < 3) ||
  1610. (venue.categories.includes("AIRPORT") && venue.lockRank < 4) ||
  1611. (venue.categories.includes("BUS_STATION") && venue.lockRank < 2) ||
  1612. (venue.categories.includes("FERRY_PIER") && venue.lockRank < 2) ||
  1613. (venue.categories.includes("JUNCTION_INTERCHANGE") &&
  1614. venue.lockRank < 2) ||
  1615. (venue.categories.includes("REST_AREAS") && venue.lockRank < 2) ||
  1616. (venue.categories.includes("SEAPORT_MARINA_HARBOR") &&
  1617. venue.lockRank < 2) ||
  1618. (venue.categories.includes("TRAIN_STATION") &&
  1619. venue.lockRank < 2) ||
  1620. (venue.categories.includes("TUNNEL") && venue.lockRank < 2) ||
  1621. (venue.categories.includes("CITY_HALL") && venue.lockRank < 2) ||
  1622. (venue.categories.includes("COLLEGE_UNIVERSITY") &&
  1623. venue.lockRank < 2) ||
  1624. (venue.categories.includes("COURTHOUSE") && venue.lockRank < 2) ||
  1625. (venue.categories.includes("DOCTOR_CLINIC") &&
  1626. venue.lockRank < 2) ||
  1627. (venue.categories.includes("EMBASSY_CONSULATE") &&
  1628. venue.lockRank < 2) ||
  1629. (venue.categories.includes("FIRE_DEPARTMENT") &&
  1630. venue.lockRank < 2) ||
  1631. (venue.categories.includes("HOSPITAL_URGENT_CARE") &&
  1632. venue.lockRank < 3) ||
  1633. (venue.categories.includes("LIBRARY") && venue.lockRank < 2) ||
  1634. (venue.categories.includes("MILITARY") && venue.lockRank < 3) ||
  1635. (venue.categories.includes("POLICE_STATION") &&
  1636. venue.lockRank < 2) ||
  1637. (venue.categories.includes("PRISON_CORRECTIONAL_FACILITY") &&
  1638. venue.lockRank < 2) ||
  1639. (venue.categories.includes("RELIGIOUS_CENTER") &&
  1640. venue.lockRank < 3) ||
  1641. (venue.categories.includes("SCHOOL") && venue.lockRank < 2) ||
  1642. (venue.categories.includes("BANK_FINANCIAL") &&
  1643. venue.lockRank < 2) ||
  1644. (venue.categories.includes("SHOPPING_CENTER") &&
  1645. venue.lockRank < 2) ||
  1646. (venue.categories.includes("MUSEUM") && venue.lockRank < 2) ||
  1647. (venue.categories.includes("RACING_TRACK") && venue.lockRank < 2) ||
  1648. (venue.categories.includes("STADIUM_ARENA") &&
  1649. venue.lockRank < 2) ||
  1650. (venue.categories.includes("THEME_PARK") && venue.lockRank < 2) ||
  1651. (venue.categories.includes("TOURIST_ATTRACTION_HISTORIC_SITE") &&
  1652. venue.lockRank < 2) ||
  1653. (venue.categories.includes("ZOO_AQUARIUM") && venue.lockRank < 2) ||
  1654. (venue.categories.includes("BEACH") && venue.lockRank < 2) ||
  1655. (venue.categories.includes("GOLF_COURSE") && venue.lockRank < 2) ||
  1656. (venue.categories.includes("PARK") && venue.lockRank < 2) ||
  1657. (venue.categories.includes("FOREST_GROVE") && venue.lockRank < 2) ||
  1658. (venue.categories.includes("ISLAND") && venue.lockRank < 4) ||
  1659. (venue.categories.includes("RIVER_STREAM") && venue.lockRank < 3) ||
  1660. (venue.categories.includes("SEA_LAKE_POOL") &&
  1661. venue.lockRank < 5) ||
  1662. (venue.categories.includes("CANAL") && venue.lockRank < 2) ||
  1663. (venue.categories.includes("SWAMP_MARSH") && venue.lockRank < 2)) {
  1664. status.priority = 2;
  1665. status.errors.push("Min lock incorrect.");
  1666. }
  1667. return status;
  1668. }
  1669. function checkPURstatus(venue) {
  1670. if (venue.venueUpdateRequests?.length > 0) {
  1671. return true;
  1672. }
  1673. else {
  1674. return false;
  1675. }
  1676. }
  1677. });
  1678. async function getAllVenues(bounds) {
  1679. let venues = [];
  1680. // console.log(bounds);
  1681. const baseURL = "https://www.waze.com/row-Descartes/app/Features?language=en&v=2&cameras=true&mapComments=true&roadClosures=true&roadTypes=1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C15%2C16%2C17%2C18%2C19%2C20%2C22&venueLevel=4&venueFilter=1%2C1%2C1%2C1&";
  1682. let urls = [];
  1683. const stepSize = 0.1;
  1684. for (let left = bounds.left; left <= bounds.right; left += stepSize) {
  1685. for (let bottom = bounds.bottom; bottom <= bounds.top; bottom += stepSize) {
  1686. urls.push(`bbox=${left}%2C${bottom}%2C${left + stepSize > bounds.right ? bounds.right : left + stepSize}%2C${bottom + stepSize > bounds.top ? bounds.top : bottom + stepSize}`);
  1687. }
  1688. }
  1689. for (let i = 0; i < urls.length; i++) {
  1690. // console.log(baseURL + urls[i]);
  1691. $("#wazemyPlaces_scanStatus").text(`Scanning tile ${i + 1} of ${urls.length}.`);
  1692. const result = await GM.xmlHttpRequest({
  1693. method: "GET",
  1694. responseType: "json",
  1695. url: baseURL + urls[i],
  1696. }).catch((e) => console.error(e));
  1697. venues = venues.concat(result.response.venues.objects);
  1698. }
  1699. return venues;
  1700. }
  1701. });
  1702. console.log("[WazeMY] PluginPlaces enabled.");
  1703. }
  1704. /**
  1705. * Disable plugin.
  1706. *
  1707. * @return {void} This function does not return anything.
  1708. */
  1709. disable() {
  1710. if ($("span[title='WazeMY Places']").length > 0) {
  1711. W.userscripts.removeSidebarTab("wazemyplaces");
  1712. }
  1713. console.log("[WazeMY] PluginPlaces disabled.");
  1714. }
  1715. /**
  1716. * Updates the settings of the PluginPlaces based on the provided settings object.
  1717. *
  1718. * @return {void} This function does not return anything.
  1719. */
  1720. updateSettings(settings) {
  1721. if (settings.enable === true) {
  1722. this.enable();
  1723. }
  1724. else {
  1725. this.disable();
  1726. }
  1727. console.log("[WazeMY] PluginPlaces settings updated.", settings);
  1728. }
  1729. }
  1730.  
  1731. ;// CONCATENATED MODULE: ./src/PluginFactory.ts
  1732.  
  1733.  
  1734.  
  1735.  
  1736.  
  1737.  
  1738. class PluginFactory {
  1739. static createPlugin(pluginName) {
  1740. switch (pluginName) {
  1741. case "PluginTooltip":
  1742. return new PluginTooltip();
  1743. case "PluginCopyLatLon":
  1744. return new PluginCopyLatLon();
  1745. case "PluginTrafficCameras":
  1746. return new PluginTrafficCameras();
  1747. case "PluginKVMR":
  1748. return new PluginKVMR();
  1749. case "PluginZoomPic":
  1750. return new PluginZoomPic();
  1751. case "PluginPlaces":
  1752. return new PluginPlaces();
  1753. default:
  1754. throw new Error(`Unknown plugin: ${pluginName}`);
  1755. }
  1756. }
  1757. }
  1758.  
  1759. ;// CONCATENATED MODULE: ./src/PluginManager.ts
  1760.  
  1761.  
  1762. class PluginManager {
  1763. constructor(settings) {
  1764. this.plugins = {};
  1765. this.settingsStorage = settings;
  1766. }
  1767. /**
  1768. * Adds a plugin to the PluginManager.
  1769. *
  1770. * @param {string} key - The key to associate the plugin with.
  1771. * @param {string} type - The type of plugin to create.
  1772. * @return {void} This function does not return anything.
  1773. */
  1774. addPlugin(key, type) {
  1775. const plugin = PluginFactory.createPlugin(type);
  1776. this.plugins[key] = plugin;
  1777. const pluginSettings = this.settingsStorage.getSetting(key);
  1778. if (pluginSettings) {
  1779. plugin.updateSettings(pluginSettings);
  1780. }
  1781. }
  1782. /**
  1783. * Removes a plugin from the PluginManager.
  1784. *
  1785. * @param {string} key - The key associated with the plugin to remove.
  1786. * @return {void} This function does not return anything.
  1787. */
  1788. removePlugin(key) {
  1789. if (this.plugins[key]) {
  1790. this.settingsStorage.removeSetting(key);
  1791. delete this.plugins[key];
  1792. }
  1793. }
  1794. /**
  1795. * Enables a plugin with the given key if it exists.
  1796. *
  1797. * @param {string} key - The key of the plugin to enable.
  1798. * @return {void} This function does not return anything.
  1799. */
  1800. enablePlugin(key) {
  1801. if (this.plugins[key]) {
  1802. this.plugins[key].enable();
  1803. }
  1804. }
  1805. /**
  1806. * Disables a plugin with the given key if it exists.
  1807. *
  1808. * @param {string} key - The key of the plugin to disable.
  1809. * @return {void} This function does not return anything.
  1810. */
  1811. disablePlugin(key) {
  1812. if (this.plugins[key]) {
  1813. this.plugins[key].disable();
  1814. }
  1815. }
  1816. /**
  1817. * Updates the settings of a plugin associated with the given key.
  1818. *
  1819. * @param {string} key - The key associated with the plugin.
  1820. * @param {any} settings - The new settings to be applied to the plugin.
  1821. * @return {void} This function does not return anything.
  1822. */
  1823. updatePluginSettings(key, settings) {
  1824. if (this.plugins[key]) {
  1825. this.plugins[key].updateSettings(settings);
  1826. this.settingsStorage.updateSetting(key, settings);
  1827. }
  1828. }
  1829. }
  1830. PluginManager.instance = new PluginManager(SettingsStorage.instance);
  1831.  
  1832. ;// CONCATENATED MODULE: ./src/index.ts
  1833.  
  1834.  
  1835. const updateMessage = `PluginZoomPic: Fix broken link after WME update.` +
  1836. `PluginTooltip: Include alternate addresses for segments.`;
  1837. async function src_main() {
  1838. console.log("[WazeMY] Script started");
  1839. document.addEventListener("wme-ready", initializeWazeMY, { once: true });
  1840. }
  1841. async function initializeWazeMY() {
  1842. console.log("[WazeMY] WME ready");
  1843. const { tabLabel, tabPane } = W.userscripts.registerSidebarTab("wazemy");
  1844. tabLabel.innerHTML = "WazeMY";
  1845. tabLabel.title = "WazeMY";
  1846. tabPane.innerHTML = `<div>
  1847. <h4>WazeMY</h4>
  1848. <b>${GM_info.script.version}</b>
  1849. </div>
  1850. <fieldset class="wazemySettings">
  1851. <legend class="wazemySettingsLegend">
  1852. <h6>Settings</h6></legend>
  1853. <div id="wazemySettings_settings"></div>
  1854. </fieldset>
  1855. <fieldset class="wazemySettings">
  1856. <legend class="wazemySettingsLegend">
  1857. <h6>Shortcuts</h6></legend>
  1858. <div id="wazemySettings_shortcuts">
  1859. </div>
  1860. </fieldset>`;
  1861. WazeWrap.Interface.ShowScriptUpdate("WME WazeMY", GM_info.script.version, updateMessage, "https://greasyfork.org/en/scripts/404584-wazemy", "javascript:alert('No forum available');");
  1862. const pluginManager = PluginManager.instance;
  1863. pluginManager.addPlugin("copylatlon", "PluginCopyLatLon");
  1864. pluginManager.addPlugin("tooltip", "PluginTooltip");
  1865. pluginManager.addPlugin("trafcam", "PluginTrafficCameras");
  1866. pluginManager.addPlugin("kvmr", "PluginKVMR");
  1867. pluginManager.addPlugin("zoompic", "PluginZoomPic");
  1868. pluginManager.addPlugin("places", "PluginPlaces");
  1869. }
  1870. src_main().catch((e) => {
  1871. console.log("WazeMY: Bootstrap");
  1872. console.log(e);
  1873. });
  1874.  
  1875. })();
  1876.  
  1877. /******/ })()
  1878. ;