WME Mobile Support

A userscript that makes the WME more useable on mobile devices

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name        WME Mobile Support
// @author      Tom 'Glodenox' Puttemans
// @namespace   http://tomputtemans.com/
// @description A userscript that makes the WME more useable on mobile devices
// @include     /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @version     0.3.6
// @supportURL  https://github.com/Glodenox/wme-mobile-support/issues
// @grant       none
// ==/UserScript==

// Style element to reuse whenever it gets removed by the WME (user login, for example)
var styleElement;

// Initialisation of the script, this will only run completely one time
function init(e) {
  if (e && e.user == null) {
    return;
  }
  // if you require certain features to be loaded, you can add them here
  if (typeof I18n === 'undefined' || typeof W === 'undefined' || typeof W.loginManager === 'undefined') {
    setTimeout(init, 200);
    return;
  }
  if (!W.loginManager.user) {
    W.loginManager.events.register("login", null, applyStyles);
    W.loginManager.events.register("loginStatus", null, applyStyles);
  }
  var viewportMeta = document.createElement('meta');
  viewportMeta.name = 'viewport';
  viewportMeta.content = 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=0';
  document.head.appendChild(viewportMeta);

  enableTouchEvents();
  //fixSelection(); // disabled as it doesn't really work as hoped
  applyStyles();
}

// Add the missing OpenLayers classes for touch navigation and enable them
function enableTouchEvents() {
  if (!OpenLayers.Control.PinchZoom) {
    OpenLayers.Control.PinchZoom=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,containerCenter:null,pinchOrigin:null,currentCenter:null,autoActivate:!0,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.handler=new OpenLayers.Handler.Pinch(this,{start:this.pinchStart,move:this.pinchMove,done:this.pinchDone},this.handlerOptions)},activate:function(){var a=OpenLayers.Control.prototype.activate.apply(this,arguments);a&&(this.map.events.on({moveend:this.updateContainerCenter,scope:this}),this.updateContainerCenter());return a},deactivate:function(){var a=OpenLayers.Control.prototype.deactivate.apply(this,arguments);this.map&&this.map.events&&this.map.events.un({moveend:this.updateContainerCenter,scope:this});return a},updateContainerCenter:function(){var a=this.map.layerContainerDiv;this.containerCenter={x:parseInt(a.style.left,10)+50,y:parseInt(a.style.top,10)+50}},pinchStart:function(a){this.currentCenter=this.pinchOrigin=a.xy},pinchMove:function(a,b){var c=b.scale,d=this.containerCenter,e=this.pinchOrigin,f=a.xy,g=Math.round(f.x-e.x+(c-1)*(d.x-e.x)),d=Math.round(f.y-e.y+(c-1)*(d.y-e.y));this.applyTransform("translate("+g+"px, "+d+"px) scale("+c+")");this.currentCenter=f},applyTransform:function(a){var b=this.map.layerContainerDiv.style;b["-webkit-transform"]=a;b["-moz-transform"]=a},pinchDone:function(a,b,c){this.applyTransform("");a=this.map.getZoomForResolution(this.map.getResolution()/c.scale,!0);if(a!==this.map.getZoom()||!this.currentCenter.equals(this.pinchOrigin)){var b=this.map.getResolutionForZoom(a),c=this.map.getLonLatFromPixel(this.pinchOrigin),d=this.currentCenter,e=this.map.getSize();c.lon+=b*(e.w/2-d.x);c.lat-=b*(e.h/2-d.y);this.map.div.clientWidth=this.map.div.clientWidth;this.map.setCenter(c,a)}},CLASS_NAME:"OpenLayers.Control.PinchZoom"});
  }
  if (!OpenLayers.Handler.Pinch) {
    OpenLayers.Handler.Pinch=OpenLayers.Class(OpenLayers.Handler,{started:!1,stopDown:!1,pinching:!1,last:null,start:null,touchstart:function(a){var b=!0;this.pinching=!1;OpenLayers.Event.isMultiTouch(a)?(this.started=!0,this.last=this.start={distance:this.getDistance(a.touches),delta:0,scale:1},this.callback("start",[a,this.start]),b=!this.stopDown):(this.started=!1,this.last=this.start=null);OpenLayers.Event.stop(a);return b},touchmove:function(a){if(this.started&&OpenLayers.Event.isMultiTouch(a)){this.pinching=!0;var b=this.getPinchData(a);this.callback("move",[a,b]);this.last=b;OpenLayers.Event.stop(a)}return!0},touchend:function(a){this.started&&(this.pinching=this.started=!1,this.callback("done",[a,this.start,this.last]),this.last=this.start=null);return!0},activate:function(){var a=!1;OpenLayers.Handler.prototype.activate.apply(this,arguments)&&(this.pinching=!1,a=!0);return a},deactivate:function(){var a=!1;OpenLayers.Handler.prototype.deactivate.apply(this,arguments)&&(this.pinching=this.started=!1,this.last=this.start=null,a=!0);return a},getDistance:function(a){var b=a[0],a=a[1];return Math.sqrt(Math.pow(b.clientX-a.clientX,2)+Math.pow(b.clientY-a.clientY,2))},getPinchData:function(a){a=this.getDistance(a.touches);return{distance:a,delta:this.last.distance-a,scale:a/this.start.distance}},CLASS_NAME:"OpenLayers.Handler.Pinch"});
  }
  W.map.navigationControl.pinchZoom = new OpenLayers.Control.PinchZoom({
    map: W.map
  });
  W.map.navigationControl.pinchZoom.activate();
}

function applyStyles() {
  if (!styleElement) {
    styleElement = document.createElement('style');
    styleElement.textContent = `@media screen and (max-width: 1000px) {
  /* Login dialog modifications */
  .modal-dialog-login { width: 100%; margin: 0 }
  .modal-dialog-login .modal-content { width: 350px }
  #login-popup { padding: 10px; width: auto }
  #login-popup .login-popup-content { display: block }
  #login-popup .login-form { padding: 15px; height: auto }
  .modal-dialog-login .login-title { font-size: 19px }
  .modal-open .edit-area > .fluid-fixed { width: 100%; height: 100vh }
  .modal-open #editor-container #WazeMap { height: calc(100vh - 46px) !important }

  /* Hide a lot of stuff */
  .login-popup-links, .language-select, .welcome-message p, .title-text, .topbar .area-managers-region, .olControlPanZoomBar, #links, #advanced-tools, .WazeControlMousePosition, #user-box, #chat-overlay, .google-permalink, .WazeControlPermalink { display: none !important }

  /* Set the default width to several objects so they don't stretch the page */
  #editor-container, #editor-container #map { min-width: auto }

  /* Adjust the sidebar and map so they appear on top of eachother */
  @media (orientation: portrait) {
    .show-sidebar .row-fluid .fluid-fixed { width: 100%; margin-left: 0 }
    .row-fluid #sidebar { width: auto }
    .edit-area { display: flex; flex-direction: column-reverse }
    #editor-container #WazeMap { height: 40vh !important }
    #editor-container:before, .show-sidebar #map, .show-sidebar #map:after { border-radius: 0 }
    .toolbar .toolbar-icon { width: 30px }
    #edit-buttons .toolbar-button { padding: 0 5px }
    .toolbar #edit-buttons .toolbar-button .item-container { padding: 0 5px }
  }

  /* Adjust the sidebar and map so they share the space horizontally */
  @media (orientation: landscape) {
    .show-sidebar .row-fluid .fluid-fixed { width: calc(100% - 330px); min-width: 50%; margin-left: 0 }
    .row-fluid #sidebar { max-width: 50% }
    #sidebar .tab-scroll-gradient { width: 100%; margin-left: -15px }
    .edit-area { display: flex }
  }

  /* Adjust toolbar */
  .toolbar #search { width: 50px; min-width: auto }
  #app-head aside #brand, .group-title { display: none }
  .toolbar .toolbar-icon { position: relative }
  #edit-buttons .toolbar-button .item-icon { display: block; top: 8px; position: relative }
  #edit-buttons .toolbar-button .menu-title { display: none }
  #edit-buttons { flex-grow: 1 }
  #edit-buttons .toolbar-submenu { margin-right: 0 }
  #toolbar .toolbar, #edit-buttons { min-width: auto }

  /* Adjust map controls */
  #WazeMap .full-screen { position: absolute; bottom: 35px; right: 5px; z-index: 1000; font-size: 1.3em }
}`;
  }
  if (!styleElement.parentNode) {
    document.head.appendChild(styleElement);
  }

  var adjustToolbar = function() {
    if (!document.querySelector('#mode-switcher') || !document.querySelector('#edit-buttons')) {
      setTimeout(adjustToolbar, 100);
      return;
    }
    document.querySelector('#mode-switcher .short-title').textContent = 'Mode';
    var addStickyClasses = function(el, classes) {
      var observer = new MutationObserver(function() {
        el.querySelector('.item-icon').classList.add(...classes);
      });
      observer.observe(el, {
        childList: true
      });
      el.querySelector('.item-icon').classList.add(...classes);
    };
    addStickyClasses(document.querySelector('#edit-buttons .toolbar-button.waze-icon-save'), ['fa', 'fa-save']);
    addStickyClasses(document.querySelector('#edit-buttons .toolbar-button.waze-icon-redo'), ['fa', 'fa-chevron-right']);
    addStickyClasses(document.querySelector('#edit-buttons .toolbar-button.waze-icon-undo'), ['fa', 'fa-chevron-left']);
  };
  adjustToolbar();

  var fullScreenButton = document.createElement('button');
  fullScreenButton.classList.add('full-screen', 'fa', 'fa-arrows-alt');
  fullScreenButton.addEventListener('click', function() {
    var docEl = document.documentElement;
    var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
    var cancelFullScreen = document.exitFullscreen || document.mozCancelFullScreen || document.webkitExitFullscreen || document.msExitFullscreen;
    if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
      requestFullScreen.call(docEl);
    } else {
      cancelFullScreen.call(document);
    }
  });
  document.querySelector('#WazeMap').appendChild(fullScreenButton);
}

init();