WME-Bootstrap

Bootstrap library for custom Waze Map Editor scripts

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/450160/1619452/WME-Bootstrap.js

  1. // ==UserScript==
  2. // @name WME Bootstrap
  3. // @version 0.2.1
  4. // @description Bootstrap library for custom Waze Map Editor scripts
  5. // @license MIT License
  6. // @author Anton Shevchuk
  7. // @namespace https://gf.qytechs.cn/users/227648-anton-shevchuk
  8. // @supportURL https://github.com/AntonShevchuk/wme-bootstrap/issues
  9. // @match https://*.waze.com/editor*
  10. // @match https://*.waze.com/*/editor*
  11. // @exclude https://*.waze.com/user/editor*
  12. // @icon https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://anton.shevchuk.name&size=64
  13. // @grant none
  14. // ==/UserScript==
  15.  
  16. /* jshint esversion: 8 */
  17. /* global jQuery, W */
  18.  
  19. (function () {
  20. 'use strict'
  21.  
  22. const SELECTORS = {
  23. node: 'div.connections-edit',
  24. segment: '#segment-edit-general',
  25. venue: '#venue-edit-general',
  26. merge: '#mergeVenuesCollection'
  27. }
  28.  
  29. class Bootstrap {
  30. /**
  31. * Bootstrap it once!
  32. */
  33. constructor () {
  34. const sandbox = typeof unsafeWindow !== 'undefined'
  35. const pageWindow = sandbox ? unsafeWindow : window
  36.  
  37. if (!pageWindow.WMEBootstrap) {
  38. pageWindow.WMEBootstrap = true
  39. document.addEventListener(
  40. 'wme-ready',
  41. () => this.init(),
  42. { once: true },
  43. );
  44. }
  45. }
  46.  
  47. /**
  48. * Initial events and handlers
  49. */
  50. init () {
  51. try {
  52. // fire `bootstrap.wme` event
  53. jQuery(document).trigger('bootstrap.wme')
  54. // setup additional handlers
  55. this.setup()
  56. // listen all events
  57. jQuery(document)
  58. .on('segment.wme', (event, element, model) => this.log('🛣️ segment.wme: ' + model.getID()))
  59. .on('segments.wme', () => this.log('🛣️️ segments.wme'))
  60. .on('node.wme', (event, element, model) => this.log('⭐️ node.wme: ' + model.getID()))
  61. .on('nodes.wme', () => this.log('⭐️ nodes.wme'))
  62. .on('venue.wme', (event, element, model) => this.log('📍️ venue.wme: ' + model.getID()))
  63. .on('venues.wme', () => this.log('🏬️ venues.wme'))
  64. .on('point.wme', () => this.log('️🏠 point.wme'))
  65. .on('place.wme', () => this.log('🏢️️ place.wme'))
  66. .on('residential.wme', () => this.log('🪧 residential.wme'))
  67. } catch (e) {
  68. console.error(e)
  69. }
  70. }
  71.  
  72. /**
  73. * Setup additional handler for `selectionchanged` event
  74. */
  75. setup () {
  76. // register handler for selection
  77. W.selectionManager.events.register(
  78. 'selectionchanged',
  79. null,
  80. () => this.handler(W.selectionManager.getSelectedDataModelObjects())
  81. )
  82. // fire handler for current selection
  83. this.handler(W.selectionManager.getSelectedDataModelObjects())
  84. }
  85.  
  86. /**
  87. * Proxy-handler
  88. * @param {Array} models
  89. */
  90. handler (models) {
  91. if (models.length === 0) {
  92. jQuery(document).trigger('none.wme')
  93. return
  94. }
  95.  
  96. let isSingle = (models.length === 1)
  97. let model = models[0]
  98.  
  99. let has = `#edit-panel:has([subtitle="ID: ${model.getID()}"]) `
  100.  
  101. switch (true) {
  102. case (model.type === 'node' && isSingle):
  103. this.trigger('node.wme', has + SELECTORS.node, model)
  104. break
  105. case (model.type === 'node'):
  106. this.trigger('nodes.wme', SELECTORS.node, models)
  107. break
  108. case (model.type === 'segment' && isSingle):
  109. this.trigger('segment.wme', has + SELECTORS.segment, model)
  110. break
  111. case (model.type === 'segment'):
  112. this
  113. .waitSegmentsCounter(models.length)
  114. .then(element => jQuery(document).trigger('segments.wme', [element, models]))
  115. break
  116. case (model.type === 'venue' && isSingle):
  117. this.trigger('venue.wme', has + SELECTORS.venue, model)
  118. if (model.isResidential()) {
  119. this.trigger('residential.wme', has + SELECTORS.venue, model)
  120. } else if (model.isPoint()) {
  121. this.trigger('point.wme', has + SELECTORS.venue, model)
  122. } else {
  123. this.trigger('place.wme', has + SELECTORS.venue, model)
  124. }
  125. break
  126. case (model.type === 'venue'):
  127. this.trigger('venues.wme', SELECTORS.merge, models)
  128. break
  129. }
  130. }
  131.  
  132. /**
  133. * Fire new event with context
  134. * It can be #node-edit-general
  135. * or #segment-edit-general
  136. * or #venue-edit-general
  137. * or #mergeVenuesCollection
  138. * @param {String} event
  139. * @param {String} selector
  140. * @param {Object|Array} models
  141. */
  142. trigger (event, selector, models) {
  143. this
  144. .waitElementBySelector(selector)
  145. .then(element => jQuery(document)
  146. .trigger(event, [element, models]))
  147. }
  148.  
  149. /**
  150. * Wait for DOM Element
  151. * @param {string} selector
  152. * @return {Promise<HTMLElement>}
  153. */
  154. waitElementBySelector (selector) {
  155. return new Promise(resolve => {
  156. if (document.querySelector(selector)) {
  157. return resolve(document.querySelector(selector))
  158. }
  159.  
  160. const observer = new MutationObserver(() => {
  161. if (document.querySelector(selector)) {
  162. resolve(document.querySelector(selector))
  163. observer.disconnect()
  164. }
  165. })
  166.  
  167. observer.observe(document.getElementById('sidebar'), {
  168. childList: true,
  169. subtree: true
  170. })
  171. })
  172. }
  173.  
  174. /**
  175. * Wait for DOM Element
  176. * @param counter
  177. * @return {Promise<HTMLElement>}
  178. */
  179. waitSegmentsCounter (counter) {
  180. let counterSelector = '#edit-panel div.segment.sidebar-column > :first-child'
  181. return new Promise(resolve => {
  182. if (
  183. document.querySelector(counterSelector)?.headline?.startsWith(counter) // beta
  184. || document.querySelector(counterSelector)?.innerText.startsWith(counter) // wme
  185. ) {
  186. return resolve(document.querySelector(SELECTORS.segment))
  187. }
  188.  
  189. const observer = new MutationObserver(() => {
  190. if (
  191. document.querySelector(counterSelector)?.headline?.startsWith(counter) // beta
  192. || document.querySelector(counterSelector)?.innerText.startsWith(counter) // wme
  193. ) {
  194. resolve(document.querySelector(SELECTORS.segment))
  195. observer.disconnect()
  196. }
  197. })
  198.  
  199. observer.observe(document.getElementById('edit-panel'), {
  200. childList: true,
  201. subtree: true
  202. })
  203. })
  204. }
  205.  
  206. /**
  207. * Just logger
  208. * @param {String} message
  209. */
  210. log (message) {
  211. console.log(
  212. '%cBootstrap:%c ' + message,
  213. 'color: #0DAD8D; font-weight: bold', 'color: dimgray; font-weight: normal'
  214. )
  215. }
  216. }
  217.  
  218. new Bootstrap()
  219.  
  220. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址