Greasy Fork 还支持 简体中文。

WME Bootstrap

Bootstrap library for custom Waze Map Editor scripts

目前為 2023-02-23 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/450160/1153357/WME%20Bootstrap.js

  1. // ==UserScript==
  2. // @name WME Bootstrap
  3. // @version 0.1.2
  4. // @description Bootstrap library for custom Waze Map Editor scripts
  5. // @license MIT License
  6. // @author Anton Shevchuk
  7. // @namespace https://greasyfork.org/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', () => this.log('🛣️ segment.wme'))
  59. .on('segments.wme', () => this.log('🛣️️ segments.wme'))
  60. .on('node.wme', () => this.log('⭐️ node.wme'))
  61. .on('nodes.wme', () => this.log('⭐️ nodes.wme'))
  62. .on('venue.wme', () => this.log('📍️ venue.wme'))
  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('selectionchanged', null, (event) => this.handler(event.selected))
  78. // fire handler for current selection
  79. this.handler(W.selectionManager.getSelectedFeatures())
  80. }
  81.  
  82. /**
  83. * Proxy-handler
  84. * @param {Array} selected
  85. */
  86. handler (selected) {
  87. if (selected.length === 0) {
  88. jQuery(document).trigger('none.wme')
  89. return
  90. }
  91.  
  92. let isSingle = (selected.length === 1)
  93. let models = selected.map(x => x.model)
  94. let model = models[0]
  95.  
  96. switch (true) {
  97. case (model.type === 'node' && isSingle):
  98. this.trigger('node.wme', SELECTORS.node, model)
  99. break
  100. case (model.type === 'node'):
  101. this.trigger('nodes.wme', SELECTORS.node, models)
  102. break
  103. case (model.type === 'segment' && isSingle):
  104. this.trigger('segment.wme', SELECTORS.segment, model)
  105. break
  106. case (model.type === 'segment'):
  107. this
  108. .waitSegmentsCounter(models.length)
  109. .then(element => jQuery(document).trigger('segments.wme', [element, models]))
  110. break
  111. case (model.type === 'venue' && isSingle):
  112. this.trigger('venue.wme', SELECTORS.venue, model)
  113. if (model.isResidential()) {
  114. this.trigger('residential.wme', SELECTORS.venue, model)
  115. } else if (model.isPoint()) {
  116. this.trigger('point.wme', SELECTORS.venue, model)
  117. } else {
  118. this.trigger('place.wme', SELECTORS.venue, model)
  119. }
  120. break
  121. case (model.type === 'venue'):
  122. this.trigger('venues.wme', SELECTORS.merge, models)
  123. break
  124. }
  125. }
  126.  
  127. /**
  128. * Fire new event with context
  129. * It can be #node-edit-general
  130. * or #segment-edit-general
  131. * or #venue-edit-general
  132. * or #mergeVenuesCollection
  133. * @param {String} event
  134. * @param {String} selector
  135. * @param {Object|Array} models
  136. */
  137. trigger (event, selector, models) {
  138. this
  139. .waitElementBySelector(selector)
  140. .then(element => jQuery(document)
  141. .trigger(event, [element, models]))
  142. }
  143.  
  144. /**
  145. * Wait for DOM Element
  146. * @param {string} selector
  147. * @return {Promise<HTMLElement>}
  148. */
  149. waitElementBySelector (selector) {
  150. return new Promise(resolve => {
  151. if (document.querySelector(selector)) {
  152. return resolve(document.querySelector(selector))
  153. }
  154.  
  155. const observer = new MutationObserver(() => {
  156. if (document.querySelector(selector)) {
  157. resolve(document.querySelector(selector))
  158. observer.disconnect()
  159. }
  160. })
  161.  
  162. observer.observe(document.getElementById('edit-panel'), {
  163. childList: true,
  164. subtree: true
  165. })
  166. })
  167. }
  168.  
  169. /**
  170. * Wait for DOM Element
  171. * @param counter
  172. * @return {Promise<HTMLElement>}
  173. */
  174. waitSegmentsCounter (counter) {
  175. let counterSelector = '#edit-panel .panel-header-component div:nth-child(2)'
  176. return new Promise(resolve => {
  177. if (document.querySelector(counterSelector)?.firstChild?.innerText.startsWith(counter)) {
  178. return resolve(document.querySelector(SELECTORS.segment))
  179. }
  180.  
  181. const observer = new MutationObserver(() => {
  182. if (document.querySelector(counterSelector)?.firstChild?.innerText.startsWith(counter)) {
  183. resolve(document.querySelector(SELECTORS.segment))
  184. observer.disconnect()
  185. }
  186. })
  187.  
  188. observer.observe(document.getElementById('edit-panel'), {
  189. childList: true,
  190. subtree: true
  191. })
  192. })
  193. }
  194.  
  195. /**
  196. * Just logger
  197. * @param {String} message
  198. */
  199. log (message) {
  200. console.log(
  201. '%cBootstrap:%c ' + message,
  202. 'color: #0DAD8D; font-weight: bold', 'color: dimgray; font-weight: normal'
  203. )
  204. }
  205. }
  206.  
  207. new Bootstrap()
  208.  
  209. })()