Advertisement
MDTech-us_MAN

Modded WME Image Overlays

Sep 19th, 2017
303
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        WME Image Overlays
  3. // @author      Tom 'Glodenox' Puttemans (Added press and hold functionality to buttons. - Maxwell175)
  4. // @namespace   http://www.tomputtemans.com/
  5. // @description Makes it possible to add images as overlay on the Waze Map Editor
  6. // @include     /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
  7. // @icon        data:image/png;base64,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
  8. // @version     0.8.3
  9. // @grant       none
  10. // ==/UserScript==
  11.  
  12. // Based on the OpenLayers.Layer.Image class
  13. OpenLayers.Layer.OverlayImage = OpenLayers.Class(OpenLayers.Layer, {
  14.   isBaseLayer: false,
  15.   url: null,
  16.   extent: null,
  17.   size: null,
  18.   tile: null,
  19.   rotation: null,
  20.   key: null,
  21.   initialize: function(name, url, extent, size, key, options) {
  22.     options = options || {};
  23.     this.url = url;
  24.     this.extent = extent;
  25.     this.maxExtent = extent;
  26.     this.size = size;
  27.     this.key = key;
  28.     this.rotation = options.rotation || 0;
  29.     OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
  30.   },
  31.   destroy: function() {
  32.     if (this.tile) {
  33.       this.removeTileMonitoringHooks(this.tile);
  34.       this.tile.destroy();
  35.       this.tile = null;
  36.     }
  37.     OpenLayers.Layer.prototype.destroy.apply(this, arguments);
  38.   },
  39.   setMap: function(map) {
  40.     OpenLayers.Layer.prototype.setMap.apply(this, arguments);
  41.   },
  42.   moveTo:function(bounds, zoomChanged, dragging) {
  43.     OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
  44.     var firstRendering = (this.tile == null);
  45.     if (zoomChanged || firstRendering) {
  46.       this.setTileSize();
  47.       var ulPx = this.map.getLayerPxFromLonLat({
  48.         lon: this.extent.left,
  49.         lat: this.extent.top
  50.       });
  51.  
  52.       if (firstRendering) {
  53.         this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, null, this.tileSize);
  54.         this.addTileMonitoringHooks(this.tile);
  55.       } else {
  56.         this.tile.size = this.tileSize.clone();
  57.         this.tile.position = ulPx.clone();
  58.       }
  59.       this.tile.draw();
  60.       if (firstRendering) {
  61.         this.setRotation(this.rotation);
  62.       }
  63.     }
  64.   },
  65.   shift: function(x, y) {
  66.     this.extent = this.extent.add(x, y);
  67.     var ulPx = this.map.getLayerPxFromLonLat({
  68.       lon: this.extent.left,
  69.       lat: this.extent.top
  70.     });
  71.     this.tile.position = ulPx.clone();
  72.     this.tile.draw();
  73.   },
  74.   scale: function(factor) {
  75.     this.extent = this.extent.scale(factor);
  76.     this.setTileSize();
  77.     var ulPx = this.map.getLayerPxFromLonLat({
  78.       lon: this.extent.left,
  79.       lat: this.extent.top
  80.     });
  81.     this.tile.position = ulPx.clone();
  82.     this.tile.size = this.tileSize.clone();
  83.     this.tile.draw();
  84.   },
  85.   rotate: function(rotation) {
  86.     this.setRotation(this.rotation + rotation);
  87.   },
  88.   setTileSize: function() {
  89.     var tileWidth = this.extent.getWidth() / this.map.getResolution();
  90.     var tileHeight = this.extent.getHeight() / this.map.getResolution();
  91.     this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
  92.   },
  93.   addTileMonitoringHooks: function(tile) {
  94.     tile.onLoadStart = function() {
  95.       this.events.triggerEvent("loadstart");
  96.     };
  97.     tile.events.register("loadstart", this, tile.onLoadStart);
  98.     tile.onLoadEnd = function() {
  99.       this.events.triggerEvent("loadend");
  100.     };
  101.     tile.events.register("loadend", this, tile.onLoadEnd);
  102.     tile.events.register("unload", this, tile.onLoadEnd);
  103.   },
  104.   removeTileMonitoringHooks: function(tile) {
  105.     tile.unload();
  106.     tile.events.un({
  107.       "loadstart": tile.onLoadStart,
  108.       "loadend": tile.onLoadEnd,
  109.       "unload": tile.onLoadEnd,
  110.       scope: this
  111.     });
  112.   },
  113.   setUrl: function(newUrl) {
  114.     this.url = newUrl;
  115.     this.tile.draw();
  116.   },
  117.   getURL: function(bounds) {
  118.     return this.url;
  119.   },
  120.   setRotation: function(rotation) {
  121.     this.rotation = rotation;
  122.     this.tile.imgDiv.style.transform = 'rotate(' + rotation + 'deg)';
  123.   },
  124.   CLASS_NAME: "OpenLayers.Layer.OverlayImage"
  125. });
  126.  
  127. function init(e) {
  128.   if (e && e.user == null) {
  129.     return;
  130.   }
  131.   if (document.getElementById('user-info') == null) {
  132.     setTimeout(init, 500);
  133.     log('user-info element not yet available, page still loading');
  134.     return;
  135.   }
  136.   if (typeof Waze.loginManager === 'undefined') {
  137.     setTimeout(init, 300);
  138.     return;
  139.   }
  140.   if (!Waze.loginManager.user) {
  141.     Waze.loginManager.events.register('login', null, init);
  142.     Waze.loginManager.events.register('loginStatus', null, init);
  143.     // Double check as event might have triggered already
  144.     if (!Waze.loginManager.user) {
  145.       return;
  146.     }
  147.   }
  148.  
  149.   var io_strings = {
  150.     en: {
  151.       tab_title: 'Image Overlays',
  152.       warning_title: 'Warning! Make sure you know which data sources you may use!',
  153.       warning_explanation1: 'As this script allows you to add external sources to add information to the map, it is important to know that not all sources can be used freely by Waze.',
  154.       warning_explanation2: 'Copying information from data sources like OpenStreetMap and Bing is <strong>strictly forbidden</strong>. It may even get you banned from editing the map as it could require Waze to remove certain sections from the map.',
  155.       safe_sources_header: 'These are usually safe sources to use:',
  156.       safe_sources_list: '<li>Information sent to you directly through mail by an organiser</li><li>Information on a governmental website</li><li>Maps found in newspapers or news sites</li><li>Data released as "open data" with no restrictions on commercial usage</li>',
  157.       community_advice: 'When in doubt about a resource, contact <a href="https://wazeopedia.waze.com/">your local community</a> and ask for their advice.',
  158.       warning_button: "I'll keep this in mind",
  159.       warning_info: 'Note: this is a one-time only warning',
  160.       add_image: 'Add image overlay',
  161.       empty_list: 'No images added yet',
  162.       name_missing: 'No name specified',
  163.       hide_overlay: 'Hide overlay',
  164.       opacity: 'Opacity',
  165.       parent_map_layer: 'Parent map layer',
  166.       parent_map_layer_help: 'This decides on top of which map layer the image overlay will be drawn',
  167.       layer_hidden: 'Hidden',
  168.       image_name: 'Name',
  169.       image_edit: 'Edit image',
  170.       image_remove: 'Remove image',
  171.       import_image: 'Import image',
  172.       import_image_description: 'You can now paste an image from your clipboard in the WME with Ctrl+V or select an image with the file input field below.',
  173.       import_error: 'Could not import image, the image is probably too big to retrieve. If you used the clipboard, you may want to download the image and try the file input field above instead.',
  174.       align_image: 'Align with map',
  175.       align_image_description: "You can use the controls below to align the image overlay with the map. Use the 'Attach to map' button above to finish.",
  176.       attach_image: 'Attach to map',
  177.       cancel: 'Cancel'
  178.     }
  179.   };
  180.   setTranslations(io_strings);
  181.  
  182.   var tab = addTab();
  183.   // Deal with events mode
  184.   if (Waze.app.modeController) {
  185.     Waze.app.modeController.model.bind('change:mode', function(model, modeId) {
  186.       if (modeId == 0) {
  187.         addTab(tab);
  188.       }
  189.     });
  190.   }
  191.  
  192.   if (localStorage.ImageOverlays_warning == undefined && Waze.loginManager.getUserRank() < 3) {
  193.     var warningMessage = document.createElement('div');
  194.     warningMessage.style.position = 'absolute';
  195.     warningMessage.style.backgroundColor = '#fff';
  196.     warningMessage.style.width = 'calc(100% - 30px)';
  197.     warningMessage.style.zIndex = '1';
  198.     var exclamationMark = document.createElement('i');
  199.     exclamationMark.className = 'fa fa-fw fa-3x fa-exclamation-triangle pull-left';
  200.     exclamationMark.style.color = '#f00';
  201.     warningMessage.appendChild(exclamationMark);
  202.     var warningTitle = document.createElement('p');
  203.     warningTitle.style.color = '#f00';
  204.     warningTitle.style.fontWeight = '700';
  205.     warningTitle.textContent = I18n.t('image_overlays.warning_title');
  206.     warningMessage.appendChild(warningTitle);
  207.     var warningExplanation1 = document.createElement('p');
  208.     warningExplanation1.textContent = I18n.t('image_overlays.warning_explanation1');
  209.     warningMessage.appendChild(warningExplanation1);
  210.     var warningExplanation2 = document.createElement('p');
  211.     warningExplanation2.innerHTML = I18n.t('image_overlays.warning_explanation2');
  212.     warningMessage.appendChild(warningExplanation2);
  213.     var safeSourcesHeader = document.createElement('p');
  214.     safeSourcesHeader.style.marginBottom = '0';
  215.     safeSourcesHeader.textContent = I18n.t('image_overlays.safe_sources_header');
  216.     warningMessage.appendChild(safeSourcesHeader);
  217.     var safeSourcesList = document.createElement('ul');
  218.     safeSourcesList.style.paddingLeft = '5%';
  219.     safeSourcesList.innerHTML = I18n.t('image_overlays.safe_sources_list');
  220.     warningMessage.appendChild(safeSourcesList);
  221.     var communityAdvice = document.createElement('p');
  222.     communityAdvice.innerHTML = I18n.t('image_overlays.community_advice');
  223.     warningMessage.appendChild(communityAdvice);
  224.     var warningButton = document.createElement('button');
  225.     warningButton.className = 'btn btn-success center-block';
  226.     warningButton.disabled = true;
  227.     warningButton.addEventListener('click', function() {
  228.       warningMessage.classList.add('hidden');
  229.       localStorage.ImageOverlays_warning = 'accepted';
  230.     });
  231.     var checkSign = document.createElement('i');
  232.     checkSign.className = 'fa fa-fw fa-check';
  233.     warningButton.appendChild(checkSign);
  234.     warningButton.appendChild(document.createTextNode(I18n.t('image_overlays.warning_button')));
  235.     var countdownSeparator = document.createTextNode(' - ');
  236.     warningButton.appendChild(countdownSeparator);
  237.     var warningCountdown = document.createElement('span');
  238.     warningCountdown.textContent = '20';
  239.     warningButton.appendChild(warningCountdown);
  240.     warningMessage.appendChild(warningButton);
  241.     var warningInfo = document.createElement('p');
  242.     warningInfo.style.fontStyle = 'italic';
  243.     warningInfo.className = 'text-center';
  244.     warningInfo.textContent = I18n.t('image_overlays.warning_info');
  245.     warningMessage.appendChild(warningInfo);
  246.     tab.appendChild(warningMessage);
  247.     var tabActivityListener = new MutationObserver(function() {
  248.       if (tab.classList.contains('active')) {
  249.         var intervalId = setInterval(function() {
  250.           if (warningCountdown.textContent > 1) {
  251.             warningCountdown.textContent = warningCountdown.textContent - 1;
  252.           } else {
  253.             clearInterval(intervalId);
  254.             warningButton.disabled = false;
  255.             warningButton.removeChild(countdownSeparator);
  256.             warningButton.removeChild(warningCountdown);
  257.           }
  258.         }, 1000);
  259.         tabActivityListener.disconnect();
  260.       }
  261.     });
  262.     tabActivityListener.observe(tab, { attributes: true, attributeFilter: [ 'class' ] });
  263.   }
  264.  
  265.   var layer = null;
  266.   var currentBlob = null;
  267.   var emptyList = document.createElement('span');
  268.   emptyList.style.fontStyle = 'italic';
  269.   emptyList.appendChild(document.createTextNode(I18n.t('image_overlays.empty_list')));
  270.   tab.appendChild(emptyList);
  271.   var imagesList = document.createElement('div');
  272.   imagesList.className = 'result-list';
  273.   imagesList.style.marginBottom = '1em';
  274.   tab.appendChild(imagesList);
  275.   getIndexedDB(function(db) {
  276.     db.transaction(['overlays'], 'readonly')
  277.       .objectStore('overlays')
  278.       .openCursor()
  279.       .addEventListener('success', function(e) {
  280.       var cursor = e.target.result;
  281.       if (cursor) {
  282.         addImageOverlay(cursor.value.name, cursor.key);
  283.         cursor.continue();
  284.       }
  285.     });
  286.   });
  287.  
  288.   var panel = document.createElement('div');
  289.   panel.className = 'hidden';
  290.   panel.style.backgroundColor = '#f2f3f4';
  291.   panel.style.padding = '15px';
  292.  
  293.   var importError = document.createElement('p');
  294.   importError.className = 'hidden text-danger';
  295.   importError.textContent = I18n.t('image_overlays.import_error');
  296.   var pasteListener = function(e) {
  297.     var items = e.clipboardData.items;
  298.     for (var i = 0; i < items.length; ++i) {
  299.       if (items[i].kind == 'file' && items[i].type.indexOf('image/') !== -1) {
  300.         var blob = items[i].getAsFile();
  301.         if (blob) {
  302.           importError.classList.add('hidden');
  303.           displayAlignPage(blob);
  304.         } else {
  305.           importError.classList.remove('hidden');
  306.           Waze.map.resize();
  307.         }
  308.         break;
  309.       }
  310.     }
  311.   };
  312.  
  313.   var cancelButton, importLabel, alignLabel, pinLabel;
  314.   cancelButton = document.createElement('button');
  315.   cancelButton.className = 'btn btn-default';
  316.   cancelButton.style.position = 'absolute';
  317.   cancelButton.style.right = '15px';
  318.   cancelButton.style.fontSize = '14px';
  319.   var cancelButtonIcon = document.createElement('i');
  320.   cancelButtonIcon.className = 'fa fa-trash-o fa-fw';
  321.   cancelButton.appendChild(cancelButtonIcon);
  322.   cancelButton.appendChild(document.createTextNode(I18n.t('image_overlays.cancel')));
  323.   cancelButton.addEventListener('click', function() {
  324.     panel.classList.add('hidden');
  325.     Waze.map.resize();
  326.     removeLayer();
  327.   });
  328.   panel.appendChild(cancelButton);
  329.   var breadcrumbs = document.createElement('div');
  330.   breadcrumbs.className = 'text-center';
  331.   breadcrumbs.style.marginBottom = '1.5em';
  332.   var breadcrumbSeparator = document.createElement('i');
  333.   breadcrumbSeparator.className = 'fa fa-fw fa-angle-right';
  334.   importLabel = createBreadcrumb('download', I18n.t('image_overlays.import_image'), 'primary');
  335.   breadcrumbs.appendChild(importLabel);
  336.   breadcrumbs.appendChild(breadcrumbSeparator);
  337.   alignLabel = createBreadcrumb('arrows-alt', I18n.t('image_overlays.align_image'), 'default');
  338.   breadcrumbs.appendChild(alignLabel);
  339.   breadcrumbs.appendChild(breadcrumbSeparator.cloneNode());
  340.   pinLabel = createBreadcrumb('map-pin', I18n.t('image_overlays.attach_image'), 'default');
  341.   breadcrumbs.appendChild(pinLabel);
  342.   panel.appendChild(breadcrumbs);
  343.   var description = document.createElement('p');
  344.   description.className = 'text-center';
  345.   panel.appendChild(description);
  346.   var instructions = document.createElement('div');
  347.   instructions.className = 'text-center';
  348.   panel.appendChild(instructions);
  349.   document.getElementById('map').insertBefore(panel, document.getElementById('map').firstChild);
  350.  
  351.   var addImageOverlayButton = document.createElement('button');
  352.   addImageOverlayButton.className = 'btn btn-primary';
  353.   var addSpan = document.createElement('span');
  354.   addSpan.className = 'fa fa-plus';
  355.   addSpan.style.marginRight = '5px';
  356.   addImageOverlayButton.appendChild(addSpan);
  357.   addImageOverlayButton.appendChild(document.createTextNode(I18n.t('image_overlays.add_image')));
  358.   addImageOverlayButton.addEventListener('click', displayImportPage);
  359.   tab.appendChild(addImageOverlayButton);
  360.   var hideOverlayButton = document.createElement('button');
  361.   hideOverlayButton.className = 'btn btn-default hidden';
  362.   hideOverlayButton.style.float = 'right';
  363.   hideOverlayButton.textContent = I18n.t('image_overlays.hide_overlay');
  364.   hideOverlayButton.addEventListener('click', removeLayer);
  365.   tab.appendChild(hideOverlayButton);
  366.  
  367.   var layerControls = document.createElement('div');
  368.   layerControls.className = 'hidden clearfix';
  369.   layerControls.style.marginTop = '8px';
  370.   var opacityRange = document.createElement('input');
  371.   opacityRange.type = 'range';
  372.   opacityRange.min = 0;
  373.   opacityRange.max = 50;
  374.   opacityRange.value = 50;
  375.   opacityRange.id = 'imageOverlaysOpacity';
  376.   var opacityLabel = document.createElement('label');
  377.   opacityLabel.textContent = I18n.t('image_overlays.opacity');
  378.   opacityLabel.htmlFor = opacityRange.id;
  379.   layerControls.appendChild(opacityLabel);
  380.   layerControls.appendChild(opacityRange);
  381.   var rangeListener = function() {
  382.     if (layer && layer.key) {
  383.       getIndexedDB(function(db) {
  384.         var objectStore = db.transaction(['overlays'], 'readwrite').objectStore('overlays');
  385.         objectStore.get(layer.key).addEventListener('success', function(e) {
  386.           var overlay = e.target.result;
  387.           overlay.opacity = opacityRange.value / 50;
  388.           objectStore.put(overlay, layer.key).addEventListener('success', function() {
  389.             layer.setOpacity(opacityRange.value / 50);
  390.           });
  391.         });
  392.       });
  393.     } else if (layer) {
  394.       layer.setOpacity(opacityRange.value / 50);
  395.     }
  396.   };
  397.   opacityRange.addEventListener('input', rangeListener);
  398.   opacityRange.addEventListener('change', rangeListener);
  399.   var parentLayer = document.createElement('select');
  400.   parentLayer.className = 'form-control';
  401.   parentLayer.id = 'imageOverlaysParentLayer';
  402.   Waze.map.events.on({
  403.     addlayer: updateParentLayer,
  404.     removelayer: updateParentLayer,
  405.     changelayer: updateParentLayer
  406.   });
  407.   parentLayer.addEventListener('change', function() {
  408.     if (layer && layer.key) {
  409.       getIndexedDB(function(db) {
  410.         var objectStore = db.transaction(['overlays'], 'readwrite').objectStore('overlays');
  411.         objectStore.get(layer.key).addEventListener('success', function(e) {
  412.           var overlay = e.target.result;
  413.           overlay.layerTarget = parentLayer.value;
  414.           objectStore.put(overlay, layer.key).addEventListener('success', function() {
  415.             var targetIndex = Waze.map.getLayerIndex(Waze.map.getLayersByName(parentLayer.value)[0]) + 1;
  416.             Waze.map.setLayerIndex(layer, targetIndex);
  417.           });
  418.         });
  419.       });
  420.     } else if (layer) {
  421.       var targetIndex = Waze.map.getLayerIndex(Waze.map.getLayersByName(parentLayer.value)[0]) + 1;
  422.       Waze.map.setLayerIndex(layer, targetIndex);
  423.     }
  424.   });
  425.   var parentLayerLabel = document.createElement('label');
  426.   parentLayerLabel.textContent = I18n.t('image_overlays.parent_map_layer') + ' ';
  427.   parentLayerLabel.htmlFor = parentLayer.id;
  428.   parentLayerLabel.style.marginTop = '10px';
  429.   var parentLayerHelp = document.createElement('i');
  430.   parentLayerHelp.className = 'waze-tooltip';
  431.   parentLayerHelp.title = I18n.t('image_overlays.parent_map_layer_help');
  432.   $(parentLayerHelp).tooltip();
  433.   parentLayerLabel.appendChild(parentLayerHelp);
  434.   layerControls.appendChild(parentLayerLabel);
  435.   layerControls.appendChild(parentLayer);
  436.   tab.appendChild(layerControls);
  437.  
  438.   var versionBlock = document.createElement('p');
  439.   versionBlock.style.fontSize = '0.9em';
  440.   versionBlock.style.marginTop = '10px';
  441.   var versionInfo = document.createElement('a');
  442.   versionInfo.appendChild(document.createTextNode(GM_info.script.name + ' (v' + GM_info.script.version + ')'));
  443.   versionInfo.href = 'https://www.waze.com/forum/viewtopic.php?f=819&t=225760';
  444.   versionInfo.target = '_blank';
  445.   versionBlock.appendChild(versionInfo);
  446.   tab.appendChild(versionBlock);
  447.  
  448.   function addImageOverlay(name, key, selected) {
  449.     emptyList.classList.add('hidden');
  450.     var container = document.createElement('div');
  451.     container.dataset.key = key;
  452.     container.className = 'result session-available';
  453.     if (selected) {
  454.       container.style.fontWeight = '700';
  455.     }
  456.     var remove = document.createElement('button');
  457.     remove.style.fontSize = '14px';
  458.     remove.style.float = 'right';
  459.     remove.style.fontWeight = 'normal';
  460.     remove.style.marginTop = '-6px';
  461.     remove.className = 'fa fa-trash-o';
  462.     remove.addEventListener('click', function(e) {
  463.       e.stopPropagation();
  464.       getIndexedDB(function(db) {
  465.         db.transaction(['overlays'], 'readwrite').objectStore('overlays').delete(key).addEventListener('success', function(e) {
  466.           container.parentNode.removeChild(container);
  467.           removeLayer();
  468.         });
  469.       });
  470.     });
  471.     container.appendChild(remove);
  472.     var nameContainer = document.createElement('div');
  473.     var rename = document.createElement('button');
  474.     rename.style.fontSize = '14px';
  475.     rename.style.float = 'right';
  476.     rename.style.fontWeight = 'normal';
  477.     rename.style.marginTop = '-6px';
  478.     rename.style.marginLeft = '4px';
  479.     rename.className = 'fa fa-pencil';
  480.     rename.addEventListener('click', function(e) {
  481.       e.stopPropagation();
  482.       getIndexedDB(function(db) {
  483.         var objectStore = db.transaction(['overlays'], 'readwrite').objectStore('overlays');
  484.         objectStore.get(key).addEventListener('success', function(e) {
  485.           var overlay = e.target.result;
  486.           var response = prompt('Please enter a new name for this image overlay', overlay.name);
  487.           if (response && response.length > 0) {
  488.             overlay.name = response;
  489.             objectStore.put(overlay, key).addEventListener('success', function() {
  490.               nameContainer.textContent = overlay.name;
  491.               nameContainer.style.fontStyle = '';
  492.             });
  493.           }
  494.         });
  495.       });
  496.     });
  497.     container.appendChild(rename);
  498.     if (name && name.length > 0) {
  499.       nameContainer.textContent = name;
  500.     } else {
  501.       nameContainer.style.fontStyle = 'italic';
  502.       nameContainer.textContent = I18n.t('image_overlays.name_missing');
  503.     }
  504.     container.appendChild(nameContainer);
  505.     container.addEventListener('click', function() {
  506.       getIndexedDB(function(db) {
  507.         db.transaction(['overlays'], 'readonly').objectStore('overlays').get(key).addEventListener('success', function(e) {
  508.           var overlay = e.target.result;
  509.           overlay.extent = new OL.Bounds(overlay.extent);
  510.           overlay.key = key;
  511.           displayImageOverlay(overlay);
  512.         });
  513.       });
  514.     });
  515.     imagesList.appendChild(container);
  516.   }
  517.  
  518.   function updateParentLayer(currentLayer) {
  519.     if (!currentLayer || typeof currentLayer == 'object') {
  520.       currentLayer = parentLayer.value;
  521.     }
  522.     while (parentLayer.firstChild) {
  523.       parentLayer.removeChild(parentLayer.firstChild);
  524.     }
  525.     Waze.map.layers.forEach(function(layer) {
  526.       if (layer.name != 'Image Overlay') {
  527.         var layerOption = document.createElement('option');
  528.         layerOption.value = layer.name;
  529.         layerOption.textContent = layer.name + (layer.visibility ? '' : ' (' + I18n.t('image_overlays.layer_hidden') + ')');
  530.         layerOption.selected = layer.name == currentLayer;
  531.         parentLayer.appendChild(layerOption);
  532.       }
  533.     });
  534.   }
  535.  
  536.   function displayImportPage() {
  537.     document.addEventListener('paste', pasteListener);
  538.     importLabel.className = 'label label-primary';
  539.     alignLabel.className = 'label label-default';
  540.     pinLabel.style.cursor = 'default';
  541.     pinLabel.removeEventListener('click', pinToMap);
  542.     removeLayer();
  543.    
  544.     description.textContent = I18n.t('image_overlays.import_image_description');
  545.     var addImageInput = document.createElement('input');
  546.     addImageInput.type = 'file';
  547.     addImageInput.accepts = 'image/*';
  548.     addImageInput.className = 'center-block';
  549.     addImageInput.addEventListener('change', function() {
  550.       displayAlignPage(addImageInput.files[0]);
  551.     });
  552.     instructions.textContent = '';
  553.     instructions.appendChild(addImageInput);
  554.     instructions.appendChild(importError);
  555.     panel.classList.remove('hidden');
  556.     Waze.map.resize();
  557.   }
  558.  
  559.   function displayAlignPage(blob) {
  560.     currentBlob = blob;
  561.     document.removeEventListener('paste', pasteListener);
  562.     importLabel.className = 'label label-success';
  563.     alignLabel.className = 'label label-primary';
  564.     parentLayer.selectedIndex = 0;
  565.  
  566.     displayImageOverlay({
  567.       'blob': blob,
  568.       'extent': Waze.map.getExtent(),
  569.       'rotation': 0
  570.     }, true);
  571.  
  572.     pinLabel.style.cursor = 'pointer';
  573.     pinLabel.addEventListener('click', pinToMap);
  574.  
  575.     description.textContent = I18n.t('image_overlays.align_image_description');
  576.     instructions.textContent = '';
  577.     instructions.appendChild(createControlButton('rotate-left', function() {
  578.       layer.rotate(-45);
  579.     }, '45°'));
  580.     instructions.appendChild(createControlButton('rotate-left', function() {
  581.       layer.rotate(-0.5);
  582.     }));
  583.     instructions.appendChild(createControlButton('arrow-up', function() {
  584.       layer.shift(0, 3 * Waze.map.getResolution());
  585.     }));
  586.     instructions.appendChild(createControlButton('rotate-right', function() {
  587.       layer.rotate(0.5);
  588.     }));
  589.     instructions.appendChild(createControlButton('rotate-right', function() {
  590.       layer.rotate(45);
  591.     }, '45°'));
  592.     instructions.appendChild(document.createElement('br'));
  593.     instructions.appendChild(createControlButton('arrow-left', function() {
  594.       layer.shift(-3 * Waze.map.getResolution(), 0);
  595.     }));
  596.     instructions.appendChild(createControlButton('crosshairs', function() {
  597.       var layerCenter = layer.extent.getCenterLonLat();
  598.       layer.shift(Waze.map.getCenter().lon - layerCenter.lon, Waze.map.getCenter().lat - layerCenter.lat);
  599.     }));
  600.     instructions.appendChild(createControlButton('arrow-right', function() {
  601.       layer.shift(3 * Waze.map.getResolution(), 0);
  602.     }));
  603.     instructions.appendChild(document.createElement('br'));
  604.     instructions.appendChild(createControlButton('minus', function() {
  605.       layer.scale(1 - (0.005 * Waze.map.getResolution()));
  606.     }));
  607.     instructions.appendChild(createControlButton('arrow-down', function() {
  608.       layer.shift(0, -3 * Waze.map.getResolution());
  609.     }));
  610.     instructions.appendChild(createControlButton('plus', function() {
  611.       layer.scale(1 + (0.005 * Waze.map.getResolution()));
  612.     }));
  613.  
  614.     Waze.map.resize();
  615.   }
  616.  
  617.   function pinToMap() {
  618.     getIndexedDB(function(db) {
  619.       var obj = {
  620.         'blob': currentBlob,
  621.         'name': currentBlob.name,
  622.         'extent': layer.extent.toArray(),
  623.         'rotation': layer.rotation,
  624.         'opacity': opacityRange.value,
  625.         'layerTarget': parentLayer.value
  626.       };
  627.       var req = db.transaction(['overlays'], 'readwrite').objectStore('overlays').add(obj);
  628.       req.addEventListener('success', function(e) {
  629.         panel.className = 'hidden';
  630.         Waze.map.resize();
  631.         addImageOverlay(obj.name, e.target.result, true);
  632.       });
  633.     });
  634.   }
  635.  
  636.   function displayImageOverlay(overlay, rescale) {
  637.     var url = window.URL.createObjectURL(overlay.blob);
  638.     var img = document.createElement('img');
  639.     img.addEventListener('load', function() {
  640.       removeLayer();
  641.       if (rescale) { // Rescale the extent for the image so it has the correct aspect ratio
  642.         var mapExtentAspectRatio = overlay.extent.getWidth() / overlay.extent.getHeight();
  643.         var imageAspectRatio = img.naturalWidth / img.naturalHeight;
  644.         if (mapExtentAspectRatio > imageAspectRatio) {
  645.           var widthDiff = overlay.extent.getWidth() - (overlay.extent.getHeight() * imageAspectRatio);
  646.           overlay.extent = new OL.Bounds([overlay.extent.left + widthDiff/2 , overlay.extent.bottom, overlay.extent.right - widthDiff/2, overlay.extent.top]);
  647.         } else {
  648.           var heightDiff = overlay.extent.getHeight() - (overlay.extent.getWidth() / imageAspectRatio);
  649.           overlay.extent = new OL.Bounds([overlay.extent.left, overlay.extent.bottom + heightDiff/2, overlay.extent.right, overlay.extent.top - heightDiff/2]);
  650.         }
  651.         overlay.extent = overlay.extent.scale(0.8);
  652.       }
  653.       layer = new OL.Layer.OverlayImage('Image Overlay', url, overlay.extent, new OL.Size(img.naturalWidth, img.naturalHeight), overlay.key, { 'rotation': overlay.rotation, 'opacity': overlay.opacity || 1 });
  654.       Waze.map.addLayer(layer);
  655.       for (var i = 0; i < imagesList.childNodes.length; i++) {
  656.         imagesList.childNodes[i].style.fontWeight = (imagesList.childNodes[i].dataset.key == overlay.key ? '700' : '');
  657.       }
  658.       layerControls.classList.remove('hidden');
  659.       hideOverlayButton.classList.remove('hidden');
  660.       opacityRange.value = (overlay.opacity ? overlay.opacity * 50 : 50);
  661.       updateParentLayer(overlay.layerTarget);
  662.       var targetIndex = Waze.map.getLayerIndex(Waze.map.getLayersByName(overlay.layerTarget || "")[0]);
  663.       if (!targetIndex || targetIndex <= 0) {
  664.         targetIndex = Waze.map.getLayerIndex(Waze.map.getLayerByUniqueName('satellite_imagery'));
  665.       }
  666.       Waze.map.setLayerIndex(layer, targetIndex+1);
  667.       Waze.map.zoomToExtent(overlay.extent);
  668.     });
  669.     img.src = url;
  670.   }
  671.  
  672.   function removeLayer() {
  673.     if (layer) {
  674.       Waze.map.removeLayer(layer);
  675.       layer = null;
  676.       layerControls.classList.add('hidden');
  677.       hideOverlayButton.classList.add('hidden');
  678.       for (var i = 0; i < imagesList.childNodes.length; i++) {
  679.         imagesList.childNodes[i].style.fontWeight = '';
  680.       }
  681.     }
  682.   }
  683. }
  684.  
  685. function setTranslations(translations) {
  686.   I18n.translations[I18n.currentLocale()].image_overlays = translations.en;
  687.   for (var i = 0; i < Object.keys(translations).length; i++) {
  688.     var locale = Object.keys(translations)[i];
  689.     if (I18n.currentLocale() == locale) {
  690.       I18n.translations[locale].image_overlays = translations[locale];
  691.       return;
  692.     }
  693.   }
  694. }
  695.  
  696. // Create a tab and possibly receive a previous tab to restore (usually in case of a mode change)
  697. function addTab(recoveredTab) {
  698.   var userInfo = document.getElementById('user-info'),
  699.       tabHandles = userInfo.querySelector('.nav-tabs'),
  700.       tabs = userInfo.querySelector('.tab-content'),
  701.       tabHandle = document.createElement('li'),
  702.       tab = document.createElement('div');
  703.   tabHandle.innerHTML = '<a href="#sidepanel-imageoverlays" data-toggle="tab" title="' + I18n.t('image_overlays.tab_title') + '"><span class="fa fa-picture-o"></span></a>';
  704.   if (recoveredTab) {
  705.     tab = recoveredTab;
  706.   } else {
  707.     tab.id = 'sidepanel-imageoverlays';
  708.     tab.className = 'tab-pane';
  709.   }
  710.   tabHandles.appendChild(tabHandle);
  711.   $(tabHandle.childNodes[0]).tooltip();
  712.   tabs.appendChild(tab);
  713.   return tab;
  714. }
  715.  
  716. function createBreadcrumb(icon, text, status) {
  717.   var label = document.createElement('span');
  718.   label.className = 'label label-' + status;
  719.   label.style.fontSize = '1.2em';
  720.   label.style.cursor = 'default';
  721.   var i = document.createElement('i');
  722.   i.className = 'fa fa-fw fa-' + icon;
  723.   label.appendChild(i);
  724.   label.appendChild(document.createTextNode(' ' + text));
  725.   return label;
  726. }
  727.  
  728. function createControlButton(icon, callback, text) {
  729.   var controlButton = document.createElement('button');
  730.   var controlButtonIcon = document.createElement('i');
  731.   controlButtonIcon.className = 'fa fa-fw fa-' + icon;
  732.   controlButton.appendChild(controlButtonIcon);
  733.   if (text) {
  734.     controlButton.appendChild(document.createTextNode(' ' + text));
  735.   }
  736.   controlButton.addEventListener('click', callback);
  737.   var interval = 0;
  738.   var timeout = 0;
  739.   controlButton.addEventListener('mousedown', function() {
  740.     timeout = setTimeout(function() {
  741.       interval = setInterval(callback, 100);
  742.     }, 500);
  743.   });
  744.   controlButton.addEventListener('mouseup', function() {
  745.     clearTimeout(timeout);
  746.     clearInterval(interval);
  747.   });
  748.   controlButton.addEventListener('mouseleave', function() {
  749.     clearTimeout(timeout);
  750.     clearInterval(interval);
  751.   });
  752.   return controlButton;
  753. }
  754.  
  755. function getIndexedDB(callback) {
  756.   var req = indexedDB.open('ImageOverlays', 1);
  757.   req.addEventListener('upgradeneeded', function(e) {
  758.     e.target.result.createObjectStore('overlays', { autoIncrement: true });
  759.   });
  760.   req.addEventListener('error', log);
  761.   req.addEventListener('success', function(e) {
  762.     callback(e.target.result);
  763.   });
  764. }
  765.  
  766. function log(message) {
  767.   if (typeof message === 'string') {
  768.     console.log('%c' + GM_info.script.name + ' (v' + GM_info.script.version + '): %c' + message, 'color:black', 'color:#d97e00');
  769.   } else {
  770.     console.log('%c' + GM_info.script.name + ' (v' + GM_info.script.version + ')', 'color:black', message);
  771.   }
  772. }
  773.  
  774. init();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement