Replace leaflet.draw with leaflet-locationfilter
authorJohn Firebaugh <john.firebaugh@gmail.com>
Fri, 19 Oct 2012 00:53:30 +0000 (17:53 -0700)
committerJohn Firebaugh <john.firebaugh@gmail.com>
Fri, 9 Nov 2012 20:59:28 +0000 (12:59 -0800)
15 files changed:
app/assets/javascripts/application.js
app/assets/javascripts/index/browse.js
app/assets/javascripts/index/export.js
app/views/browse/start.html.erb
app/views/layouts/_head.html.erb
config/environments/production.rb
config/locales/en.yml
vendor/assets/leaflet/img/filter-icon.png [new file with mode: 0644]
vendor/assets/leaflet/img/move-handle.png [new file with mode: 0644]
vendor/assets/leaflet/img/resize-handle.png [new file with mode: 0644]
vendor/assets/leaflet/leaflet.draw.css [deleted file]
vendor/assets/leaflet/leaflet.draw.ie.css [deleted file]
vendor/assets/leaflet/leaflet.draw.js [deleted file]
vendor/assets/leaflet/leaflet.locationfilter.css [new file with mode: 0644]
vendor/assets/leaflet/leaflet.locationfilter.js [new file with mode: 0644]

index da05801a5fc4ca9a0b1c1041d2963ad690004d58..4fa2b56cca377c34afb57c61f8aa7f2fbe00f012 100644 (file)
@@ -6,7 +6,7 @@
 //= require augment
 //= require leaflet
 //= require leaflet.osm
-//= require leaflet.draw
+//= require leaflet.locationfilter
 //= require i18n/translations
 //= require osm
 //= require piwik
index 65ed5f785301208f7865f6842d8b44aeb4a44814..0016c7b5d1c0ad35af2983886649cce0f8815fd7 100644 (file)
@@ -11,7 +11,6 @@ $(document).ready(function () {
   });
 
   function startBrowse(sidebarHtml) {
-    var browseMode = "auto";
     var browseBounds;
     var layersById;
     var selectedLayer;
@@ -41,8 +40,11 @@ $(document).ready(function () {
       return !areasHidden && L.OSM.DataLayer.prototype.isWayArea.apply(this, arguments);
     };
 
-    var drawHandler = new L.Rectangle.Draw(map, {title: I18n.t('browse.start_rjs.drag_a_box')});
-    map.on('draw:rectangle-created', endDrag);
+    var locationFilter = new L.LocationFilter({
+      enableButton: false,
+      adjustButton: false,
+      onChange: getData
+    }).addTo(map);
 
     $("#sidebar_title").html(I18n.t('browse.start_rjs.data_frame_title'));
     $("#sidebar_content").html(sidebarHtml);
@@ -52,15 +54,19 @@ $(document).ready(function () {
     map.on("moveend", updateData);
     updateData();
 
-    $("#browse_select_view").click(useMap);
+    $("#browse_filter_toggle").toggle(enableFilter, disableFilter);
 
     $("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.hide_areas'));
     $("#browse_hide_areas_box").toggle(hideAreas, showAreas);
 
     function updateData() {
-      if (browseMode == "auto") {
+      if (!locationFilter.isEnabled()) {
         if (map.getZoom() >= 15) {
-          useMap();
+          var bounds = map.getBounds();
+          if (!browseBounds || !browseBounds.contains(bounds)) {
+            browseBounds = bounds;
+            getData();
+          }
         } else {
           setStatus(I18n.t('browse.start_rjs.zoom_or_select'));
         }
@@ -69,62 +75,34 @@ $(document).ready(function () {
 
     $("#sidebar").one("closed", function () {
       map.removeLayer(dataLayer);
+      map.removeLayer(locationFilter);
       map.off("moveend", updateData);
-      map.off('draw:rectangle-created', endDrag);
-      drawHandler.disable();
-    });
-
-    $("#browse_select_box").click(function () {
-      $("#browse_select_box").html(I18n.t('browse.start_rjs.drag_a_box'));
-
-      drawHandler.enable();
-
-      return false;
     });
 
-    function useMap() {
-      var bounds = map.getBounds();
-
-      if (!browseBounds || !browseBounds.contains(bounds)) {
-        browseBounds = bounds;
-        browseMode = "auto";
-
-        getData();
-
-        $("#browse_select_view").hide();
-      }
+    function enableFilter() {
+      $("#browse_filter_toggle").html(I18n.t('browse.start_rjs.view_data'));
+      locationFilter.enable();
+      getData();
+    }
 
-      return false;
+    function disableFilter() {
+      $("#browse_filter_toggle").html(I18n.t('browse.start_rjs.manually_select'));
+      locationFilter.disable();
+      getData();
     }
 
     function hideAreas() {
       $("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.show_areas'));
-
       areasHidden = true;
-
       getData();
     }
 
     function showAreas() {
       $("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.hide_areas'));
-
       areasHidden = false;
-
       getData();
     }
 
-    function endDrag(e) {
-      browseBounds = e.rect.getBounds();
-      browseMode = "manual";
-
-      getData();
-
-      $("#browse_select_box").html(I18n.t('browse.start_rjs.manually_select'));
-      $("#browse_select_view").show();
-
-      drawHandler.disable();
-    }
-
     function displayFeatureWarning(count, limit, callback) {
       clearStatus();
 
@@ -145,7 +123,8 @@ $(document).ready(function () {
     }
 
     function getData() {
-      var size = browseBounds.getSize();
+      var bounds = locationFilter.isEnabled() ? locationFilter.getBounds() : map.getBounds();
+      var size = bounds.getSize();
 
       if (size > OSM.MAX_REQUEST_AREA) {
         setStatus(I18n.t("browse.start_rjs.unable_to_load_size", { max_bbox_size: OSM.MAX_REQUEST_AREA, bbox_size: size }));
@@ -154,7 +133,7 @@ $(document).ready(function () {
 
       setStatus(I18n.t('browse.start_rjs.loading'));
 
-      var url = "/api/" + OSM.API_VERSION + "/map?bbox=" + browseBounds.toBBOX();
+      var url = "/api/" + OSM.API_VERSION + "/map?bbox=" + bounds.toBBOX();
 
       /*
        * Modern browsers are quite happy showing far more than 100 features in
@@ -211,7 +190,7 @@ $(document).ready(function () {
 
       onSelect(layer);
 
-      if (browseMode != "auto") {
+      if (locationFilter.isEnabled()) {
         map.panTo(layer.getBounds().getCenter());
       }
 
index bff1ab239bd2a39c3e027cb0c1be1b67104ec6c0..e1b079607fed9e301e7d78f74fdf055a9386143f 100644 (file)
@@ -11,10 +11,13 @@ $(document).ready(function () {
   }
 
   function startExport(sidebarHtml) {
-    var marker, rectangle;
+    var marker;
 
-    var drawHandler = new L.Rectangle.Draw(map, {title: I18n.t('export.start_rjs.drag_a_box')});
-    map.on('draw:rectangle-created', endDrag);
+    var locationFilter = new L.LocationFilter({
+      enableButton: false,
+      adjustButton: false,
+      onChange: filterChanged
+    }).addTo(map);
 
     map.on("moveend", mapMoved);
     map.on("baselayerchange", htmlUrlChanged);
@@ -24,7 +27,7 @@ $(document).ready(function () {
 
     $("#maxlat,#minlon,#maxlon,#minlat").change(boundsChanged);
 
-    $("#drag_box").click(startDrag);
+    $("#drag_box").click(enableFilter);
 
     $("#add_marker").click(startMarker);
 
@@ -46,14 +49,11 @@ $(document).ready(function () {
     $("#sidebar").one("closed", function () {
       $("body").removeClass("site-export").addClass("site-index");
 
-      clearBox();
+      map.removeLayer(locationFilter);
       clearMarker();
 
       map.off("moveend", mapMoved);
       map.off("baselayerchange", htmlUrlChanged);
-      map.off('draw:rectangle-created', endDrag);
-
-      drawHandler.disable();
     });
 
     function getBounds() {
@@ -80,36 +80,22 @@ $(document).ready(function () {
     function boundsChanged() {
       var bounds = getBounds();
 
-      map.off("moveend", mapMoved);
       map.fitBounds(bounds);
 
-      clearBox();
-      drawBox(bounds);
+      enableFilter();
+      locationFilter.setBounds(bounds);
 
       validateControls();
       mapnikSizeChanged();
     }
 
-    function startDrag() {
-      $("#drag_box").html(I18n.t('export.start_rjs.drag_a_box'));
-
-      clearBox();
-      drawHandler.enable();
-    }
-
-    function endDrag(e) {
-      var bounds = e.rect.getBounds();
-
-      map.off("moveend", mapMoved);
-      setBounds(bounds);
-      drawBox(bounds);
-      validateControls();
-
-      $("#drag_box").html(I18n.t('export.start_rjs.manually_select'));
+    function enableFilter() {
+      $("#drag_box").hide();
+      locationFilter.enable();
     }
 
-    function transformComplete(event) {
-      setBounds(event.feature.geometry.bounds);
+    function filterChanged() {
+      setBounds(locationFilter.getBounds());
       validateControls();
     }
 
@@ -152,8 +138,10 @@ $(document).ready(function () {
     }
 
     function mapMoved() {
-      setBounds(map.getBounds());
-      validateControls();
+      if (!locationFilter.isEnabled()) {
+        setBounds(map.getBounds());
+        validateControls();
+      }
     }
 
     function setBounds(bounds) {
@@ -168,18 +156,6 @@ $(document).ready(function () {
       htmlUrlChanged();
     }
 
-    function clearBox() {
-      if (rectangle) {
-        map.removeLayer(rectangle);
-      }
-      rectangle = null;
-    }
-
-    function drawBox(bounds) {
-      rectangle = L.rectangle(bounds);
-      rectangle.addTo(map);
-    }
-
     function validateControls() {
       var bounds = getBounds();
 
index 3777d710a3c04fdad917db673ed06374a23eee28..b0315dad99ba6155b14fa7cfe2be35581065806e 100644 (file)
@@ -1,11 +1,9 @@
 <div>
   <div style="text-align: center">
     <p style="margin-top: 10px; margin-bottom: 20px">
-      <a id="browse_select_view" href="#"><%= t'browse.start.view_data' %></a>
+      <a id="browse_filter_toggle" href="#"><%= t'browse.start_rjs.manually_select' %></a>
       <br />
-      <a id="browse_select_box" href="#"><%= t'browse.start.manually_select' %></a>  
-      <br />
-      <a id="browse_hide_areas_box" href="#"><%= t'browse.start.hide_areas' %></a>  
+      <a id="browse_hide_areas_box" href="#"><%= t'browse.start_rjs.hide_areas' %></a>
     </p>
   </div>
   <div id="browse_status" style="text-align: center; display: none">
index 0461c93e5335a0c4f89aedb6c6edb3b0c766977f..5688c9ebdf18ed4a76a51243f7d81d6fec219e60 100644 (file)
@@ -6,10 +6,9 @@
   <%= stylesheet_link_tag "large-#{dir}", :media => "screen and (min-width: 642px)" %>
   <%= stylesheet_link_tag "print-#{dir}", :media => "print" %>
   <%= stylesheet_link_tag "leaflet" %>
-  <%= stylesheet_link_tag "leaflet.draw" %>
+  <%= stylesheet_link_tag "leaflet.locationfilter" %>
   <!--[if IE]>
     <%= stylesheet_link_tag "leaflet.ie" %>
-    <%= stylesheet_link_tag "leaflet.draw.ie" %>
     <%= stylesheet_link_tag "large-#{dir}", :media => "screen" %>
   <![endif]--> <!-- IE is totally broken with CSS media queries -->
   <%= favicon_link_tag "favicon.ico" %>
index fc728929a71665336b62e95fd98ed17c2724585e..cdedbeafb903576fd4387a878ecb49c6db7afa0e 100644 (file)
@@ -61,7 +61,7 @@ OpenStreetMap::Application.configure do
   config.assets.precompile += %w( large-ltr.css small-ltr.css print-ltr.css )
   config.assets.precompile += %w( large-rtl.css small-rtl.css print-rtl.css )
   config.assets.precompile += %w( browse.css theme/openstreetmap/style.css )
-  config.assets.precompile += %w( leaflet.css leaflet.ie.css leaflet.draw.css leaflet.draw.ie.css )
+  config.assets.precompile += %w( leaflet.css leaflet.ie.css leaflet.locationfilter.css )
 
   # Disable delivery errors, bad email addresses will be ignored
   # config.action_mailer.raise_delivery_errors = false
index aa3149e9101b08b3a5932b62776fc266e6f93c9f..ee9165787b1ab84f04120dcc75a4570b320e78bd 100644 (file)
@@ -219,14 +219,11 @@ en:
       relation_title: "Relation: %{relation_name}"
       download_xml: "Download XML"
       view_history: "View history"
-    start:
-      view_data: "View data for current map view"
-      manually_select: "Manually select a different area"
     start_rjs:
       data_layer_name: "Browse Map Data"
       data_frame_title: "Data"
       zoom_or_select: "Zoom in or select an area of the map to view"
-      drag_a_box: "Drag a box on the map to select an area"
+      view_data: "View data for current map view"
       manually_select: "Manually select a different area"
       hide_areas: "Hide areas"
       show_areas: "Show areas"
diff --git a/vendor/assets/leaflet/img/filter-icon.png b/vendor/assets/leaflet/img/filter-icon.png
new file mode 100644 (file)
index 0000000..981d82a
Binary files /dev/null and b/vendor/assets/leaflet/img/filter-icon.png differ
diff --git a/vendor/assets/leaflet/img/move-handle.png b/vendor/assets/leaflet/img/move-handle.png
new file mode 100644 (file)
index 0000000..d6b557a
Binary files /dev/null and b/vendor/assets/leaflet/img/move-handle.png differ
diff --git a/vendor/assets/leaflet/img/resize-handle.png b/vendor/assets/leaflet/img/resize-handle.png
new file mode 100644 (file)
index 0000000..6caa604
Binary files /dev/null and b/vendor/assets/leaflet/img/resize-handle.png differ
diff --git a/vendor/assets/leaflet/leaflet.draw.css b/vendor/assets/leaflet/leaflet.draw.css
deleted file mode 100644 (file)
index 566ba32..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* Leaflet controls */
-
-.leaflet-control-draw {
-       background-color: rgba(0, 0, 0, 0.25);
-       -webkit-border-radius: 7px;
-          -moz-border-radius: 7px;
-               border-radius: 7px;
-       padding: 5px;
-}
-
-.leaflet-control-draw a {
-       background-color: rgba(255, 255, 255, 0.75);
-       background-position: 50% 50%;
-       background-repeat: no-repeat;
-       -webkit-border-radius: 4px;
-          -moz-border-radius: 4px;
-               border-radius: 4px;
-       display: block;
-       margin-top: 5px;
-       width: 19px;
-       height: 19px;
-}
-
-.leaflet-control-draw a:first-child{
-       margin-top: 0;
-}      
-
-.leaflet-control-draw a:hover {
-       background-color: #fff;
-}
-
-.leaflet-touch .leaflet-control-draw a {
-       width: 27px;
-       height: 27px;
-}      
-
-.leaflet-control-draw-polyline {
-       background-image: url(images/draw-polyline.png);
-}
-
-.leaflet-control-draw-polygon {
-       background-image: url(images/draw-polygon.png);
-}
-
-.leaflet-control-draw-rectangle {
-       background-image: url(images/draw-rectangle.png);
-}
-
-.leaflet-control-draw-circle {
-       background-image: url(images/draw-circle.png);
-}
-
-.leaflet-control-draw-marker {
-       background-image: url(images/draw-marker-icon.png);
-}
-
-.leaflet-mouse-marker {
-       background-color: #fff;
-       cursor: crosshair;
-}
-
-.leaflet-draw-label {
-       background-color: #fff;
-       border: 1px solid #ccc;
-       color: #222;
-       font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
-       margin-left: 20px;
-       margin-top: -21px;
-       padding: 2px 4px;
-       position: absolute;
-       white-space: nowrap;
-       z-index: 6;
-}
-
-.leaflet-error-draw-label {
-       background-color: #F2DEDE;
-       border-color: #E6B6BD;
-       color: #B94A48;
-}
-
-.leaflet-draw-label-single {
-       margin-top: -12px
-}
-
-.leaflet-draw-label-subtext {
-       color: #999;
-}
-
-.leaflet-draw-guide-dash {
-       font-size: 1%;
-       opacity: 0.6;
-       position: absolute;
-       width: 5px;
-       height: 5px;
-}
-
-.leaflet-flash-anim {
-       -webkit-animation-duration: 0.66s;
-          -moz-animation-duration: 0.66s;
-            -o-animation-duration: 0.66s;
-               animation-duration: 0.66s;
-       -webkit-animation-fill-mode: both;
-          -moz-animation-fill-mode: both;
-            -o-animation-fill-mode: both;
-               animation-fill-mode: both;
-       -webkit-animation-name: leaflet-flash;
-          -moz-animation-name: leaflet-flash;
-            -o-animation-name: leaflet-flash;
-               animation-name: leaflet-flash;
-}
-
-@-webkit-keyframes leaflet-flash {
-       0%, 50%, 100% { opacity: 1; }   
-       25%, 75% { opacity: 0.3; }
-}
-
-@-moz-keyframes leaflet-flash {
-       0%, 50%, 100% { opacity: 1; }   
-       25%, 75% { opacity: 0.3; }
-}
-
-@-o-keyframes leaflet-flash {
-       0%, 50%, 100% { opacity: 1; }   
-       25%, 75% { opacity: 0.3; }
-}
-
-@keyframes leaflet-flash {
-       0%, 50%, 100% { opacity: 1; }   
-       25%, 75% { opacity: 0; }
-}
\ No newline at end of file
diff --git a/vendor/assets/leaflet/leaflet.draw.ie.css b/vendor/assets/leaflet/leaflet.draw.ie.css
deleted file mode 100644 (file)
index f057184..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Conditional stylesheet for IE. */
-
-.leaflet-control-draw {
-       filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#3F000000', EndColorStr='#3F000000');
-}
-
-.leaflet-control-draw a {
-       background-color: #eee;
-       filter: alpha(opacity=90);
-}
\ No newline at end of file
diff --git a/vendor/assets/leaflet/leaflet.draw.js b/vendor/assets/leaflet/leaflet.draw.js
deleted file mode 100644 (file)
index cd2af02..0000000
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- Copyright (c) 2012, Smartrak, Jacob Toye
- Leaflet.draw is an open-source JavaScript library for drawing shapes/markers on leaflet powered maps.
- https://github.com/jacobtoye/Leaflet.draw
-*/
-(function (window, undefined) {
-
-L.drawVersion = '0.1.4';
-
-L.Util.extend(L.LineUtil, {
-       // Checks to see if two line segments intersect. Does not handle degenerate cases.
-       // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf
-       segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {
-               return  this._checkCounterclockwise(p, p2, p3) !==
-                               this._checkCounterclockwise(p1, p2, p3) &&
-                               this._checkCounterclockwise(p, p1, p2) !==
-                               this._checkCounterclockwise(p, p1, p3);
-       },
-
-       // check to see if points are in counterclockwise order
-       _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
-               return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);
-       }
-});
-
-L.Polyline.include({
-       // Check to see if this polyline has any linesegments that intersect.
-       // NOTE: does not support detecting intersection for degenerate cases.
-       intersects: function () {
-               var points = this._originalPoints,
-                       len = points ? points.length : 0,
-                       i, j, p, p1, p2, p3;
-
-               if (this._tooFewPointsForIntersection()) {
-                       return false;
-               }
-
-               for (i = len - 1; i >= 3; i--) {
-                       p = points[i - 1];
-                       p1 = points[i];
-
-                       
-                       if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {
-                               return true;
-                       }
-               }
-
-               return false;
-       },
-
-       // Check for intersection if new latlng was added to this polyline.
-       // NOTE: does not support detecting intersection for degenerate cases.
-       newLatLngIntersects: function (latlng, skipFirst) {
-               // Cannot check a polyline for intersecting lats/lngs when not added to the map
-               if (!this._map) {
-                       return false;
-               }
-
-               return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);
-       },
-
-       // Check for intersection if new point was added to this polyline.
-       // newPoint must be a layer point.
-       // NOTE: does not support detecting intersection for degenerate cases.
-       newPointIntersects: function (newPoint, skipFirst) {
-               var points = this._originalPoints,
-                       len = points ? points.length : 0,
-                       lastPoint = points ? points[len - 1] : null,
-                       // The previous previous line segment. Previous line segement doesn't need testing.
-                       maxIndex = len - 2;
-
-               if (this._tooFewPointsForIntersection(1)) {
-                       return false;
-               }
-
-               return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);
-       },
-
-       // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).
-       // Cannot have intersection when < 3 line segments (< 4 points)
-       _tooFewPointsForIntersection: function (extraPoints) {
-               var points = this._originalPoints,
-                       len = points ? points.length : 0;
-               // Increment length by extraPoints if present
-               len += extraPoints || 0;
-
-               return !this._originalPoints || len <= 3;
-       },
-
-       // Checks a line segment intersections with any line segements before its predecessor.
-       // Don't need to check the predecessor as will never intersect.
-       _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {
-               var points = this._originalPoints,
-                       p2, p3;
-
-               minIndex = minIndex || 0;
-
-               // Check all previous line segments (beside the immediately previous) for intersections
-               for (var j = maxIndex; j > minIndex; j--) {
-                       p2 = points[j - 1];
-                       p3 = points[j];
-
-                       if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {
-                               return true;
-                       }
-               }
-
-               return false;
-       }
-});
-
-L.Polygon.include({
-       // Checks a polygon for any intersecting line segments. Ignores holes.
-       intersects: function () {
-               var polylineIntersects,
-                       points = this._originalPoints,
-                       len, firstPoint, lastPoint, maxIndex;
-
-               if (this._tooFewPointsForIntersection()) {
-                       return false;
-               }
-
-               polylineIntersects = L.Polyline.prototype.intersects.call(this);
-
-               // If already found an intersection don't need to check for any more.
-               if (polylineIntersects) {
-                       return true;
-               }
-
-               len = points.length;
-               firstPoint = points[0];
-               lastPoint = points[len - 1];
-               maxIndex = len - 2;
-
-               // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)
-               return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);
-       }
-});
-
-L.Handler.Draw = L.Handler.extend({
-       includes: L.Mixin.Events,
-
-       initialize: function (map, options) {
-               this._map = map;
-               this._container = map._container;
-               this._overlayPane = map._panes.overlayPane;
-               this._popupPane = map._panes.popupPane;
-
-               // Merge default shapeOptions options with custom shapeOptions
-               if (options && options.shapeOptions) {
-                       options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
-               }
-               L.Util.extend(this.options, options);
-       },
-
-       enable: function () {
-               this.fire('activated');
-               L.Handler.prototype.enable.call(this);
-       },
-       
-       addHooks: function () {
-               if (this._map) {
-                       L.DomUtil.disableTextSelection();
-
-                       this._label = L.DomUtil.create('div', 'leaflet-draw-label', this._popupPane);
-                       this._singleLineLabel = false;
-
-                       L.DomEvent.addListener(this._container, 'keyup', this._cancelDrawing, this);
-               }
-       },
-
-       removeHooks: function () {
-               if (this._map) {
-                       L.DomUtil.enableTextSelection();
-
-                       this._popupPane.removeChild(this._label);
-                       delete this._label;
-
-                       L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing);
-               }
-       },
-
-       _updateLabelText: function (labelText) {
-               labelText.subtext = labelText.subtext || '';
-
-               // update the vertical position (only if changed)
-               if (labelText.subtext.length === 0 && !this._singleLineLabel) {
-                       L.DomUtil.addClass(this._label, 'leaflet-draw-label-single');
-                       this._singleLineLabel = true;
-               }
-               else if (labelText.subtext.length > 0 && this._singleLineLabel) {
-                       L.DomUtil.removeClass(this._label, 'leaflet-draw-label-single');
-                       this._singleLineLabel = false;
-               }
-
-               this._label.innerHTML =
-                       (labelText.subtext.length > 0 ? '<span class="leaflet-draw-label-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
-                       '<span>' + labelText.text + '</span>';
-       },
-
-       _updateLabelPosition: function (pos) {
-               L.DomUtil.setPosition(this._label, pos);
-       },
-
-       // Cancel drawing when the escape key is pressed
-       _cancelDrawing: function (e) {
-               if (e.keyCode === 27) {
-                       this.disable();
-               }
-       }
-});
-
-L.Polyline.Draw = L.Handler.Draw.extend({
-       Poly: L.Polyline,
-
-       options: {
-               allowIntersection: true,
-               drawError: {
-                       color: '#b00b00',
-                       message: '<strong>Error:</strong> shape edges cannot cross!',
-                       timeout: 2500
-               },
-               icon: new L.DivIcon({
-                       iconSize: new L.Point(8, 8),
-                       className: 'leaflet-div-icon leaflet-editing-icon'
-               }),
-               guidelineDistance: 20,
-               shapeOptions: {
-                       stroke: true,
-                       color: '#f06eaa',
-                       weight: 4,
-                       opacity: 0.5,
-                       fill: false,
-                       clickable: true
-               },
-               zIndexOffset: 2000 // This should be > than the highest z-index any map layers
-       },
-
-       initialize: function (map, options) {
-               // Merge default drawError options with custom options
-               if (options && options.drawError) {
-                       options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
-               }
-               L.Handler.Draw.prototype.initialize.call(this, map, options);
-       },
-       
-       addHooks: function () {
-               L.Handler.Draw.prototype.addHooks.call(this);
-               if (this._map) {
-                       this._markers = [];
-
-                       this._markerGroup = new L.LayerGroup();
-                       this._map.addLayer(this._markerGroup);
-
-                       this._poly = new L.Polyline([], this.options.shapeOptions);
-
-                       this._updateLabelText(this._getLabelText());
-
-                       // Make a transparent marker that will used to catch click events. These click
-                       // events will create the vertices. We need to do this so we can ensure that
-                       // we can create vertices over other map layers (markers, vector layers). We
-                       // also do not want to trigger any click handlers of objects we are clicking on
-                       // while drawing.
-                       if (!this._mouseMarker) {
-                               this._mouseMarker = L.marker(this._map.getCenter(), {
-                                       icon: L.divIcon({
-                                               className: 'leaflet-mouse-marker',
-                                               iconAnchor: [20, 20],
-                                               iconSize: [40, 40]
-                                       }),
-                                       opacity: 0,
-                                       zIndexOffset: this.options.zIndexOffset
-                               });
-                       }
-
-                       this._mouseMarker
-                               .on('click', this._onClick, this)
-                               .addTo(this._map);
-
-                       this._map.on('mousemove', this._onMouseMove, this);
-               }
-       },
-
-       removeHooks: function () {
-               L.Handler.Draw.prototype.removeHooks.call(this);
-
-               this._clearHideErrorTimeout();
-
-               this._cleanUpShape();
-               
-               // remove markers from map
-               this._map.removeLayer(this._markerGroup);
-               delete this._markerGroup;
-               delete this._markers;
-
-               this._map.removeLayer(this._poly);
-               delete this._poly;
-
-               this._mouseMarker.off('click', this._onClick);
-               this._map.removeLayer(this._mouseMarker);
-               delete this._mouseMarker;
-
-               // clean up DOM
-               this._clearGuides();
-
-               this._map.off('mousemove', this._onMouseMove);
-       },
-
-       _finishShape: function () {
-               if (!this.options.allowIntersection && this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], true)) {
-                       this._showErrorLabel();
-                       return;
-               }
-               if (!this._shapeIsValid()) {
-                       this._showErrorLabel();
-                       return;
-               }
-
-               this._map.fire(
-                       'draw:poly-created',
-                       { poly: new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions) }
-               );
-               this.disable();
-       },
-
-       //Called to verify the shape is valid when the user tries to finish it
-       //Return false if the shape is not valid
-       _shapeIsValid: function () {
-               return true;
-       },
-
-       _onMouseMove: function (e) {
-               var newPos = e.layerPoint,
-                       latlng = e.latlng,
-                       markerCount = this._markers.length;
-
-               // Save latlng
-               this._currentLatLng = latlng;
-
-               // update the label
-               this._updateLabelPosition(newPos);
-
-               if (markerCount > 0) {
-                       this._updateLabelText(this._getLabelText());
-                       // draw the guide line
-                       this._clearGuides();
-                       this._drawGuide(
-                               this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
-                               newPos
-                       );
-               }
-
-               // Update the mouse marker position
-               this._mouseMarker.setLatLng(latlng);
-
-               L.DomEvent.preventDefault(e.originalEvent);
-       },
-
-       _onClick: function (e) {
-               var latlng = e.target.getLatLng(),
-                       markerCount = this._markers.length;
-
-               if (markerCount > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
-                       this._showErrorLabel();
-                       return;
-               }
-               else if (this._errorShown) {
-                       this._hideErrorLabel();
-               }
-
-               this._markers.push(this._createMarker(latlng));
-
-               this._poly.addLatLng(latlng);
-
-               if (this._poly.getLatLngs().length === 2) {
-                       this._map.addLayer(this._poly);
-               }
-
-               this._updateMarkerHandler();
-
-               this._vertexAdded(latlng);
-       },
-
-       _updateMarkerHandler: function () {
-               // The last marker shold have a click handler to close the polyline
-               if (this._markers.length > 1) {
-                       this._markers[this._markers.length - 1].on('click', this._finishShape, this);
-               }
-               
-               // Remove the old marker click handler (as only the last point should close the polyline)
-               if (this._markers.length > 2) {
-                       this._markers[this._markers.length - 2].off('click', this._finishShape);
-               }
-       },
-       
-       _createMarker: function (latlng) {
-               var marker = new L.Marker(latlng, {
-                       icon: this.options.icon,
-                       zIndexOffset: this.options.zIndexOffset * 2
-               });
-               
-               this._markerGroup.addLayer(marker);
-
-               return marker;
-       },
-
-       _drawGuide: function (pointA, pointB) {
-               var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
-                       i,
-                       fraction,
-                       dashPoint,
-                       dash;
-
-               //create the guides container if we haven't yet (TODO: probaly shouldn't do this every time the user starts to draw?)
-               if (!this._guidesContainer) {
-                       this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
-               }
-       
-               //draw a dash every GuildeLineDistance
-               for (i = this.options.guidelineDistance; i < length; i += this.options.guidelineDistance) {
-                       //work out fraction along line we are
-                       fraction = i / length;
-
-                       //calculate new x,y point
-                       dashPoint = {
-                               x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
-                               y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
-                       };
-
-                       //add guide dash to guide container
-                       dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
-                       dash.style.backgroundColor =
-                               !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;
-
-                       L.DomUtil.setPosition(dash, dashPoint);
-               }
-       },
-
-       _updateGuideColor: function (color) {
-               if (this._guidesContainer) {
-                       for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
-                               this._guidesContainer.childNodes[i].style.backgroundColor = color;
-                       }
-               }
-       },
-
-       // removes all child elements (guide dashes) from the guides container
-       _clearGuides: function () {
-               if (this._guidesContainer) {
-                       while (this._guidesContainer.firstChild) {
-                               this._guidesContainer.removeChild(this._guidesContainer.firstChild);
-                       }
-               }
-       },
-
-       _updateLabelText: function (labelText) {
-               if (!this._errorShown) {
-                       L.Handler.Draw.prototype._updateLabelText.call(this, labelText);
-               }
-       },
-
-       _getLabelText: function () {
-               var labelText,
-                       distance,
-                       distanceStr;
-
-               if (this._markers.length === 0) {
-                       labelText = {
-                               text: 'Click to start drawing line.'
-                       };
-               } else {
-                       // calculate the distance from the last fixed point to the mouse position
-                       distance = this._measurementRunningTotal + this._currentLatLng.distanceTo(this._markers[this._markers.length - 1].getLatLng());
-                       // show metres when distance is < 1km, then show km
-                       distanceStr = distance  > 1000 ? (distance  / 1000).toFixed(2) + ' km' : Math.ceil(distance) + ' m';
-                       
-                       if (this._markers.length === 1) {
-                               labelText = {
-                                       text: 'Click to continue drawing line.',
-                                       subtext: distanceStr
-                               };
-                       } else {
-                               labelText = {
-                                       text: 'Click last point to finish line.',
-                                       subtext: distanceStr
-                               };
-                       }
-               }
-               return labelText;
-       },
-
-       _showErrorLabel: function () {
-               this._errorShown = true;
-
-               // Update label
-               L.DomUtil.addClass(this._label, 'leaflet-error-draw-label');
-               L.DomUtil.addClass(this._label, 'leaflet-flash-anim');
-               L.Handler.Draw.prototype._updateLabelText.call(this, { text: this.options.drawError.message });
-
-               // Update shape
-               this._updateGuideColor(this.options.drawError.color);
-               this._poly.setStyle({ color: this.options.drawError.color });
-
-               // Hide the error after 2 seconds
-               this._clearHideErrorTimeout();
-               this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorLabel, this), this.options.drawError.timeout);
-       },
-
-       _hideErrorLabel: function () {
-               this._errorShown = false;
-
-               this._clearHideErrorTimeout();
-               
-               // Revert label
-               L.DomUtil.removeClass(this._label, 'leaflet-error-draw-label');
-               L.DomUtil.removeClass(this._label, 'leaflet-flash-anim');
-               this._updateLabelText(this._getLabelText());
-
-               // Revert shape
-               this._updateGuideColor(this.options.shapeOptions.color);
-               this._poly.setStyle({ color: this.options.shapeOptions.color });
-       },
-
-       _clearHideErrorTimeout: function () {
-               if (this._hideErrorTimeout) {
-                       clearTimeout(this._hideErrorTimeout);
-                       this._hideErrorTimeout = null;
-               }
-       },
-
-       _vertexAdded: function (latlng) {
-               if (this._markers.length === 1) {
-                       this._measurementRunningTotal = 0;
-               }
-               else {
-                       this._measurementRunningTotal +=
-                               latlng.distanceTo(this._markers[this._markers.length - 2].getLatLng());
-               }
-       },
-
-       _cleanUpShape: function () {
-               if (this._markers.length > 0) {
-                       this._markers[this._markers.length - 1].off('click', this._finishShape);
-               }
-       }
-});
-
-L.Polygon.Draw = L.Polyline.Draw.extend({
-       Poly: L.Polygon,
-
-       options: {
-               shapeOptions: {
-                       stroke: true,
-                       color: '#f06eaa',
-                       weight: 4,
-                       opacity: 0.5,
-                       fill: true,
-                       fillColor: null, //same as color by default
-                       fillOpacity: 0.2,
-                       clickable: false
-               }
-       },
-
-       _updateMarkerHandler: function () {
-               // The first marker shold have a click handler to close the polygon
-               if (this._markers.length === 1) {
-                       this._markers[0].on('click', this._finishShape, this);
-               }
-       },
-
-       _getLabelText: function () {
-               var text;
-               if (this._markers.length === 0) {
-                       text = 'Click to start drawing shape.';
-               } else if (this._markers.length < 3) {
-                       text = 'Click to continue drawing shape.';
-               } else {
-                       text = 'Click first point to close this shape.';
-               }
-               return {
-                       text: text
-               };
-       },
-
-       _shapeIsValid: function () {
-               return this._markers.length >= 3;
-       },
-
-       _vertexAdded: function (latlng) {
-               //calc area here
-       },
-
-       _cleanUpShape: function () {
-               if (this._markers.length > 0) {
-                       this._markers[0].off('click', this._finishShape);
-               }
-       }
-});
-
-L.SimpleShape = {};
-
-L.SimpleShape.Draw = L.Handler.Draw.extend({
-       addHooks: function () {
-               L.Handler.Draw.prototype.addHooks.call(this);
-               if (this._map) {
-                       this._map.dragging.disable();
-                       //TODO refactor: move cursor to styles
-                       this._container.style.cursor = 'crosshair';
-
-                       this._updateLabelText({ text: this._initialLabelText });
-
-                       this._map
-                               .on('mousedown', this._onMouseDown, this)
-                               .on('mousemove', this._onMouseMove, this);
-
-               }
-       },
-
-       removeHooks: function () {
-               L.Handler.Draw.prototype.removeHooks.call(this);
-               if (this._map) {
-                       this._map.dragging.enable();
-                       //TODO refactor: move cursor to styles
-                       this._container.style.cursor = '';
-
-                       this._map
-                               .off('mousedown', this._onMouseDown, this)
-                               .off('mousemove', this._onMouseMove, this);
-
-                       L.DomEvent.off(document, 'mouseup', this._onMouseUp);
-
-                       // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
-                       if (this._shape) {
-                               this._map.removeLayer(this._shape);
-                               delete this._shape;
-                       }
-               }
-               this._isDrawing = false;
-       },
-
-       _onMouseDown: function (e) {
-               this._isDrawing = true;
-               this._startLatLng = e.latlng;
-
-               L.DomEvent
-                       .on(document, 'mouseup', this._onMouseUp, this)
-                       .preventDefault(e.originalEvent);
-       },
-
-       _onMouseMove: function (e) {
-               var layerPoint = e.layerPoint,
-                               latlng = e.latlng;
-
-               this._updateLabelPosition(layerPoint);
-               if (this._isDrawing) {
-                       this._updateLabelText({ text: 'Release mouse to finish drawing.' });
-                       this._drawShape(latlng);
-               }
-       },
-
-       _onMouseUp: function (e) {
-               if (this._shape) {
-                       this._fireCreatedEvent();
-               }
-               
-               this.disable();
-       }
-});
-
-L.Circle.Draw = L.SimpleShape.Draw.extend({
-       options: {
-               shapeOptions: {
-                       stroke: true,
-                       color: '#f06eaa',
-                       weight: 4,
-                       opacity: 0.5,
-                       fill: true,
-                       fillColor: null, //same as color by default
-                       fillOpacity: 0.2,
-                       clickable: true
-               }
-       },
-
-       _initialLabelText: 'Click and drag to draw circle.',
-
-       _drawShape: function (latlng) {
-               if (!this._shape) {
-                       this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(latlng), this.options.shapeOptions);
-                       this._map.addLayer(this._shape);
-               } else {
-                       this._shape.setRadius(this._startLatLng.distanceTo(latlng));
-               }
-       },
-
-       _fireCreatedEvent: function () {
-               this._map.fire(
-                       'draw:circle-created',
-                       { circ: new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions) }
-               );
-       }
-});
-
-L.Rectangle.Draw = L.SimpleShape.Draw.extend({
-       options: {
-               shapeOptions: {
-                       stroke: true,
-                       color: '#f06eaa',
-                       weight: 4,
-                       opacity: 0.5,
-                       fill: true,
-                       fillColor: null, //same as color by default
-                       fillOpacity: 0.2,
-                       clickable: true
-               }
-       },
-       
-       _initialLabelText: 'Click and drag to draw rectangle.',
-
-       _drawShape: function (latlng) {
-               if (!this._shape) {
-                       this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
-                       this._map.addLayer(this._shape);
-               } else {
-                       this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
-               }
-       },
-
-       _fireCreatedEvent: function () {
-               this._map.fire(
-                       'draw:rectangle-created',
-                       { rect: new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions) }
-               );
-       }
-});
-
-L.Marker.Draw = L.Handler.Draw.extend({
-       options: {
-               icon: new L.Icon.Default(),
-               zIndexOffset: 2000 // This should be > than the highest z-index any markers
-       },
-       
-       addHooks: function () {
-               L.Handler.Draw.prototype.addHooks.call(this);
-               
-               if (this._map) {
-                       this._updateLabelText({ text: 'Click map to place marker.' });
-                       this._map.on('mousemove', this._onMouseMove, this);
-               }
-       },
-
-       removeHooks: function () {
-               L.Handler.Draw.prototype.removeHooks.call(this);
-               
-               if (this._map) {
-                       if (this._marker) {
-                               this._marker.off('click', this._onClick);
-                               this._map
-                                       .off('click', this._onClick)
-                                       .removeLayer(this._marker);
-                               delete this._marker;
-                       }
-
-                       this._map.off('mousemove', this._onMouseMove);
-               }
-       },
-
-       _onMouseMove: function (e) {
-               var newPos = e.layerPoint,
-                       latlng = e.latlng;
-
-               this._updateLabelPosition(newPos);
-
-               if (!this._marker) {
-                       this._marker = new L.Marker(latlng, {
-                               icon: this.options.icon,
-                               zIndexOffset: this.options.zIndexOffset
-                       });
-                       // Bind to both marker and map to make sure we get the click event.
-                       this._marker.on('click', this._onClick, this);
-                       this._map
-                               .on('click', this._onClick, this)
-                               .addLayer(this._marker);
-               }
-               else {
-                       this._marker.setLatLng(latlng);
-               }
-       },
-
-       _onClick: function (e) {
-               this._map.fire(
-                       'draw:marker-created',
-                       { marker: new L.Marker(this._marker.getLatLng(), { icon: this.options.icon }) }
-               );
-               this.disable();
-       }
-});
-
-L.Map.mergeOptions({
-       drawControl: false
-});
-
-L.Control.Draw = L.Control.extend({
-
-       options: {
-               position: 'topleft',
-               polyline: {
-                       title: 'Draw a polyline'
-               },
-               polygon: {
-                       title: 'Draw a polygon'
-               },
-               rectangle: {
-                       title: 'Draw a rectangle'
-               },
-               circle: {
-                       title: 'Draw a circle'
-               },
-               marker: {
-                       title: 'Add a marker'
-               }
-       },
-
-       handlers: {},
-       
-       initialize: function (options) {
-               L.Util.extend(this.options, options);
-       },
-       
-       onAdd: function (map) {
-               var className = 'leaflet-control-draw',
-                       container = L.DomUtil.create('div', className);
-
-               if (this.options.polyline) {
-                       this.handlers.polyline = new L.Polyline.Draw(map, this.options.polyline);
-                       this._createButton(
-                               this.options.polyline.title,
-                               className + '-polyline',
-                               container,
-                               this.handlers.polyline.enable,
-                               this.handlers.polyline
-                       );
-                       this.handlers.polyline.on('activated', this._disableInactiveModes, this);
-               }
-
-               if (this.options.polygon) {
-                       this.handlers.polygon = new L.Polygon.Draw(map, this.options.polygon);
-                       this._createButton(
-                               this.options.polygon.title,
-                               className + '-polygon',
-                               container,
-                               this.handlers.polygon.enable,
-                               this.handlers.polygon
-                       );
-                       this.handlers.polygon.on('activated', this._disableInactiveModes, this);
-               }
-
-               if (this.options.rectangle) {
-                       this.handlers.rectangle = new L.Rectangle.Draw(map, this.options.rectangle);
-                       this._createButton(
-                               this.options.rectangle.title,
-                               className + '-rectangle',
-                               container,
-                               this.handlers.rectangle.enable,
-                               this.handlers.rectangle
-                       );
-                       this.handlers.rectangle.on('activated', this._disableInactiveModes, this);
-               }
-
-               if (this.options.circle) {
-                       this.handlers.circle = new L.Circle.Draw(map, this.options.circle);
-                       this._createButton(
-                               this.options.circle.title,
-                               className + '-circle',
-                               container,
-                               this.handlers.circle.enable,
-                               this.handlers.circle
-                       );
-                       this.handlers.circle.on('activated', this._disableInactiveModes, this);
-               }
-
-               if (this.options.marker) {
-                       this.handlers.marker = new L.Marker.Draw(map, this.options.marker);
-                       this._createButton(
-                               this.options.marker.title,
-                               className + '-marker',
-                               container,
-                               this.handlers.marker.enable,
-                               this.handlers.marker
-                       );
-                       this.handlers.marker.on('activated', this._disableInactiveModes, this);
-               }
-               
-               return container;
-       },
-
-       _createButton: function (title, className, container, fn, context) {
-               var link = L.DomUtil.create('a', className, container);
-               link.href = '#';
-               link.title = title;
-
-               L.DomEvent
-                       .on(link, 'click', L.DomEvent.stopPropagation)
-                       .on(link, 'mousedown', L.DomEvent.stopPropagation)
-                       .on(link, 'dblclick', L.DomEvent.stopPropagation)
-                       .on(link, 'click', L.DomEvent.preventDefault)
-                       .on(link, 'click', fn, context);
-
-               return link;
-       },
-
-       // Need to disable the drawing modes if user clicks on another without disabling the current mode
-       _disableInactiveModes: function () {
-               for (var i in this.handlers) {
-                       // Check if is a property of this object and is enabled
-                       if (this.handlers.hasOwnProperty(i) && this.handlers[i].enabled()) {
-                               this.handlers[i].disable();
-                       }
-               }
-       }
-});
-
-L.Map.addInitHook(function () {
-       if (this.options.drawControl) {
-               this.drawControl = new L.Control.Draw();
-               this.addControl(this.drawControl);
-       }
-});
-
-
-
-}(this));
\ No newline at end of file
diff --git a/vendor/assets/leaflet/leaflet.locationfilter.css b/vendor/assets/leaflet/leaflet.locationfilter.css
new file mode 100644 (file)
index 0000000..2698d78
--- /dev/null
@@ -0,0 +1,78 @@
+div.leaflet-marker-icon.location-filter.resize-marker {
+    background: url( img/resize-handle.png ) no-repeat;  
+    cursor: move;
+}
+div.leaflet-marker-icon.location-filter.move-marker {
+    background: url( img/move-handle.png ) no-repeat;  
+    cursor: move;
+}
+
+div.location-filter.button-container {
+    background: #bfbfbf;
+    background: rgba(0, 0, 0, 0.25);
+    -moz-border-radius: 7px;
+    -webkit-border-radius: 7px;
+    border-radius: 7px;
+    padding: 5px;
+}
+.leaflet-container div.location-filter.button-container a {
+    display: inline-block;
+    color: #0F2416;
+    font-size: 11px;
+    font-weight: normal;
+    text-shadow: #A1BB9C 0 1px;
+    padding: 6px 7px;
+    border: 1px solid #9CC5A4;
+    -moz-border-radius: 3px;
+    -webkit-border-radius: 3px;
+    border-radius: 3px;
+    -webkit-box-shadow: inset rgba(255,255,255,0.75) 0 1px 1px;
+    -moz-box-shadow: inset rgba(255,255,255,0.75) 0 1px 1px;
+    box-shadow: inset rgba(255,255,255,0.75) 0 1px 1px;    
+    background: #c4e3b9;
+    background: rgba(218, 252, 205, 0.9);
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(218, 252, 205, 0.9)), color-stop(100%, rgba(173, 226, 176, 0.9)));
+    background: -webkit-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background: -moz-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background: -ms-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background: -o-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background: linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+}
+.leaflet-container div.location-filter.button-container a:hover {
+    color: #263F1C;
+    background: #dde6d8;
+    background: rgba(245, 255, 240, 0.9);
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(245, 255, 240, 0.9)), color-stop(100%, rgba(203, 228, 205, 0.9)));
+    background: -webkit-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background: -moz-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background: -ms-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background: -o-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background: linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+}
+
+.leaflet-container div.location-filter.button-container a.enable-button {
+    padding: 6px 7px 6px 25px;
+    background-image: url( img/filter-icon.png ), -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(218, 252, 205, 0.9)), color-stop(100%, rgba(173, 226, 176, 0.9)));
+    background-image: url( img/filter-icon.png ), -webkit-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), -moz-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), -ms-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), -o-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+    background-repeat: no-repeat;
+    background-position: left center;
+}
+.leaflet-container div.location-filter.button-container a.enable-button:hover,
+.leaflet-container div.location-filter.button-container.enabled a.enable-button {
+    background-image: url( img/filter-icon.png ), -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(245, 255, 240, 0.9)), color-stop(100%, rgba(203, 228, 205, 0.9)));
+    background-image: url( img/filter-icon.png ), -webkit-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), -moz-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), -ms-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), -o-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background-image: url( img/filter-icon.png ), linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+    background-repeat: no-repeat;
+    background-position: left center;
+}
+
+.leaflet-container div.location-filter.button-container a.adjust-button {
+    margin-left: 2px;
+}
diff --git a/vendor/assets/leaflet/leaflet.locationfilter.js b/vendor/assets/leaflet/leaflet.locationfilter.js
new file mode 100644 (file)
index 0000000..f4e2568
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * Leaflet.locationfilter - leaflet location filter plugin
+ * Copyright (C) 2012, Tripbirds.com
+ * http://tripbirds.com
+ *
+ * Licensed under the MIT License.
+ *
+ * Date: 2012-09-24
+ * Version: 0.1
+ */
+L.LatLngBounds.prototype.modify = function(map, amount) {
+    var sw = this.getSouthWest(),
+        ne = this.getNorthEast(),
+        swPoint = map.latLngToLayerPoint(sw),
+        nePoint = map.latLngToLayerPoint(ne);
+
+    sw = map.layerPointToLatLng(new L.Point(swPoint.x-amount, swPoint.y+amount));
+    ne = map.layerPointToLatLng(new L.Point(nePoint.x+amount, nePoint.y-amount));
+    
+    return new L.LatLngBounds(sw, ne);
+};
+
+L.Control.Button = L.Class.extend({
+    initialize: function(options) {
+        L.Util.setOptions(this, options);
+    },
+
+    addTo: function(container) {
+        container.addButton(this);
+        return this;
+    },
+    
+    onAdd: function (buttonContainer) {
+        this._buttonContainer = buttonContainer;
+        this._button = L.DomUtil.create('a', this.options.className, this._buttonContainer.getContainer());
+        this._button.href = '#';
+        this.setText(this.options.text);
+
+        var that = this;
+        this._onClick = function(event) {
+            that.options.onClick.call(that, event);
+        };
+
+        L.DomEvent
+            .on(this._button, 'click', L.DomEvent.stopPropagation)
+            .on(this._button, 'mousedown', L.DomEvent.stopPropagation)
+            .on(this._button, 'dblclick', L.DomEvent.stopPropagation)
+            .on(this._button, 'click', L.DomEvent.preventDefault)
+            .on(this._button, 'click', this._onClick, this);
+    },
+
+    remove: function() {
+        L.DomEvent.off(this._button, "click", this._onClick);
+        this._buttonContainer.getContainer().removeChild(this._button);
+    },
+
+    setText: function(text) {
+        this._button.title = text;
+        this._button.innerHTML = text;
+    }
+});
+
+L.Control.ButtonContainer = L.Control.extend({
+    options: {
+        position: 'topleft'
+    },
+
+    getContainer: function() {
+        if (!this._container) {
+            this._container = L.DomUtil.create('div', this.options.className);
+        }
+        return this._container;
+    },
+
+    onAdd: function (map) {
+        this._map = map;
+        return this.getContainer();
+    },
+
+    addButton: function(button) {
+        button.onAdd(this);
+    },
+
+    addClass: function(className) {
+        L.DomUtil.addClass(this.getContainer(), className);
+    },
+
+    removeClass: function(className) {
+        L.DomUtil.removeClass(this.getContainer(), className);
+    }
+});
+
+L.LocationFilter = L.Class.extend({
+    options: {
+        enableButton: {
+            enableText: "Select area",
+            disableText: "Remove selection"
+        },
+        adjustButton: {
+            text: "Select area within current zoom"
+        }
+    },
+
+    initialize: function(options) {
+        L.Util.setOptions(this, options);
+    },
+
+    addTo: function(map) {
+        map.addLayer(this);
+        return this;
+    },
+
+    onAdd: function(map) {
+        this._map = map;
+        this._layer = new L.LayerGroup();
+        this._initializeButtonContainer();
+
+        if (this.options.enable) {
+            this.enable();
+        }
+    },
+
+    onRemove: function(map) {
+        this.disable();
+        this._buttonContainer.removeFrom(map);
+    },
+
+    /* Get the current filter bounds */
+    getBounds: function() { 
+        return new L.LatLngBounds(this._sw, this._ne); 
+    },
+
+    setBounds: function(bounds) {
+        this._nw = bounds.getNorthWest();
+        this._ne = bounds.getNorthEast();
+        this._sw = bounds.getSouthWest();
+        this._se = bounds.getSouthEast();
+        this._draw();
+        this._callCallback("onChange");
+    },
+
+    isEnabled: function() {
+        return this._enabled;
+    },
+
+    /* Draw a rectangle */
+    _drawRectangle: function(bounds, options) {
+        options = options || {};
+        var defaultOptions = {
+            stroke: false,
+            fill: true,
+            fillColor: "black",
+            fillOpacity: 0.3,
+            clickable: false
+        };
+        options = L.Util.extend(defaultOptions, options);
+        var rect = new L.Rectangle(bounds, options);
+        rect.addTo(this._layer);
+        return rect;
+    },
+
+    /* Draw a draggable marker */
+    _drawImageMarker: function(point, options) {
+        var marker = new L.Marker(point, {
+            icon: new L.DivIcon({
+                iconAnchor: options.anchor,
+                iconSize: options.size,
+                className: options.className
+            }),
+            draggable: true
+        });
+        marker.addTo(this._layer);
+        return marker;
+    },
+
+    /* Draw a move marker. Sets up drag listener that updates the
+       filter corners and redraws the filter when the move marker is
+       moved */
+    _drawMoveMarker: function(point) {
+        var that = this;
+        this._moveMarker = this._drawImageMarker(point, {
+            "className": "location-filter move-marker",
+            "anchor": [-10, -10],
+            "size": [13,13]
+        });
+        this._moveMarker.on('drag', function(e) {
+            var markerPos = that._moveMarker.getLatLng(),
+                latDelta = markerPos.lat-that._nw.lat,
+                lngDelta = markerPos.lng-that._nw.lng;
+            that._nw = new L.LatLng(that._nw.lat+latDelta, that._nw.lng+lngDelta);
+            that._ne = new L.LatLng(that._ne.lat+latDelta, that._ne.lng+lngDelta);
+            that._sw = new L.LatLng(that._sw.lat+latDelta, that._sw.lng+lngDelta);
+            that._se = new L.LatLng(that._se.lat+latDelta, that._se.lng+lngDelta);
+            that._draw();
+        });
+        this._setupDragendListener(this._moveMarker);
+        return this._moveMarker;
+    },
+
+    /* Draw a resize marker */
+    _drawResizeMarker: function(point, latFollower, lngFollower, otherPos) {
+        return this._drawImageMarker(point, {
+            "className": "location-filter resize-marker",
+            "anchor": [7, 6],
+            "size": [13, 12] 
+        });
+    },
+
+    /* Track moving of the given resize marker and update the markers
+       given in options.moveAlong to match the position of the moved
+       marker. Update filter corners and redraw the filter */
+    _setupResizeMarkerTracking: function(marker, options) {
+        var that = this;
+        marker.on('drag', function(e) {
+            var curPosition = marker.getLatLng(),
+                latMarker = options.moveAlong.lat,
+                lngMarker = options.moveAlong.lng;
+            // Move follower markers when this marker is moved
+            latMarker.setLatLng(new L.LatLng(curPosition.lat, latMarker.getLatLng().lng));
+            lngMarker.setLatLng(new L.LatLng(lngMarker.getLatLng().lat, curPosition.lng));
+            // Sort marker positions in nw, ne, sw, se order
+            var corners = [that._nwMarker.getLatLng(), 
+                           that._neMarker.getLatLng(), 
+                           that._swMarker.getLatLng(), 
+                           that._seMarker.getLatLng()];
+            corners.sort(function(a, b) {
+                if (a.lat != b.lat)
+                    return b.lat-a.lat;
+                else
+                    return a.lng-b.lng;
+            });
+            // Update corner points and redraw everything except the resize markers
+            that._nw = corners[0];
+            that._ne = corners[1];
+            that._sw = corners[2];
+            that._se = corners[3];
+            that._draw({repositionResizeMarkers: false});
+        });
+        this._setupDragendListener(marker);
+    },
+
+    /* Call the callback (given by name) if it was supplied in options */
+    _callCallback: function(callbackName) {
+        if (this.options[callbackName]) {
+            this.options[callbackName](this.getBounds());
+        }
+    },
+
+    /* Call the onChange callback whenever dragend is triggered on the
+       given marker */
+    _setupDragendListener: function(marker) {
+        if (this.options.onChange) {
+            var that = this;
+            marker.on('dragend', function(e) {
+                that._callCallback("onChange");
+            });
+        }
+    },
+
+    /* Create bounds for the mask rectangles and the location
+       filter rectangle */
+    _calculateBounds: function() {
+        var mapBounds = this._map.getBounds(),
+            outerBounds = new L.LatLngBounds(
+                new L.LatLng(mapBounds.getSouthWest().lat-0.1,
+                             mapBounds.getSouthWest().lng-0.1),
+                new L.LatLng(mapBounds.getNorthEast().lat+0.1,
+                             mapBounds.getNorthEast().lng+0.1)
+            );
+
+        // The south west and north east points of the mask */
+        this._osw = outerBounds.getSouthWest();
+        this._one = outerBounds.getNorthEast();
+
+        // Bounds for the mask rectangles
+        this._northBounds = new L.LatLngBounds(new L.LatLng(this._ne.lat, this._osw.lng), this._one),
+        this._westBounds = new L.LatLngBounds(new L.LatLng(this._sw.lat, this._osw.lng), this._nw),
+        this._eastBounds = new L.LatLngBounds(this._se, new L.LatLng(this._ne.lat, this._one.lng)),
+        this._southBounds = new L.LatLngBounds(this._osw, new L.LatLng(this._sw.lat, this._one.lng));
+    },
+
+    /* Initializes rectangles and markers */
+    _initialDraw: function() {
+        if (this._initialDrawCalled) {
+            return;
+        }
+
+        // Calculate filter bounds
+        this._calculateBounds();
+
+        // Create rectangles
+        this._northRect = this._drawRectangle(this._northBounds);
+        this._westRect = this._drawRectangle(this._westBounds);
+        this._eastRect = this._drawRectangle(this._eastBounds);
+        this._southRect = this._drawRectangle(this._southBounds);
+        this._innerRect = this._drawRectangle(this.getBounds(), {
+            fillColor: "transparent",
+            stroke: true,
+            color: "white",
+            weight: 1,
+            opacity: 0.9
+        });
+
+        // Create resize markers
+        this._nwMarker = this._drawResizeMarker(this._nw);
+        this._neMarker = this._drawResizeMarker(this._ne);
+        this._swMarker = this._drawResizeMarker(this._sw);
+        this._seMarker = this._drawResizeMarker(this._se);
+
+        // Setup tracking of resize markers. Each marker has pair of
+        // follower markers that must be moved whenever the marker is
+        // moved. For example, whenever the north west resize marker
+        // moves, the south west marker must move along on the x-axis
+        // and the north east marker must move on the y axis
+        this._setupResizeMarkerTracking(this._nwMarker, {moveAlong: {lat: this._neMarker, lng: this._swMarker}});
+        this._setupResizeMarkerTracking(this._neMarker, {moveAlong: {lat: this._nwMarker, lng: this._seMarker}});
+        this._setupResizeMarkerTracking(this._swMarker, {moveAlong: {lat: this._seMarker, lng: this._nwMarker}});
+        this._setupResizeMarkerTracking(this._seMarker, {moveAlong: {lat: this._swMarker, lng: this._neMarker}});
+
+        // Create move marker
+        this._moveMarker = this._drawMoveMarker(this._nw);
+
+        this._initialDrawCalled = true;
+    },
+
+    /* Reposition all rectangles and markers to the current filter bounds. */    
+    _draw: function(options) {
+        options = L.Util.extend({repositionResizeMarkers: true}, options);
+
+        // Calculate filter bounds
+        this._calculateBounds();
+
+        // Reposition rectangles
+        this._northRect.setBounds(this._northBounds);
+        this._westRect.setBounds(this._westBounds);
+        this._eastRect.setBounds(this._eastBounds);
+        this._southRect.setBounds(this._southBounds);
+        this._innerRect.setBounds(this.getBounds());
+
+        // Reposition resize markers
+        if (options.repositionResizeMarkers) {
+            this._nwMarker.setLatLng(this._nw);
+            this._neMarker.setLatLng(this._ne);
+            this._swMarker.setLatLng(this._sw);
+            this._seMarker.setLatLng(this._se);
+        }
+
+        // Reposition the move marker
+        this._moveMarker.setLatLng(this._nw);
+    }, 
+
+    /* Adjust the location filter to the current map bounds */
+    _adjustToMap: function() {
+        this.setBounds(this._map.getBounds());
+        this._map.zoomOut();
+    },
+
+    /* Enable the location filter */
+    enable: function() {
+        if (this._enabled) {
+            return;
+        }
+
+        // Initialize corners
+        var bounds;
+        if (this._sw && this._ne) {
+            bounds = new L.LatLngBounds(this._sw, this._ne);
+        } else if (this.options.bounds) {
+            bounds = this.options.bounds;
+        } else {
+            bounds = this._map.getBounds();
+        }
+        this._map.invalidateSize();
+        this._nw = bounds.getNorthWest();
+        this._ne = bounds.getNorthEast();
+        this._sw = bounds.getSouthWest();
+        this._se = bounds.getSouthEast();
+            
+
+        // Update buttons
+        this._buttonContainer.addClass("enabled");
+
+        if (this._enableButton) {
+            this._enableButton.setText(this.options.enableButton.disableText);
+        }
+
+        if (this.options.adjustButton) {
+            this._createAdjustButton();
+        }
+        
+        // Draw filter
+        this._initialDraw();
+        this._draw();
+
+        // Set up map move event listener
+        var that = this;
+        this._moveHandler = function() {
+            that._draw();
+        };
+        this._map.on("move", this._moveHandler);
+
+        // Add the filter layer to the map
+        this._layer.addTo(this._map);
+        
+        // Zoom out the map if necessary
+        var mapBounds = this._map.getBounds();
+        bounds = new L.LatLngBounds(this._sw, this._ne).modify(this._map, 10);
+        if (!mapBounds.contains(bounds.getSouthWest()) || !mapBounds.contains(bounds.getNorthEast())) {
+            this._map.fitBounds(bounds);
+        }
+
+        this._enabled = true;
+        
+        // Call the enabled callback
+        this._callCallback("onEnabled");
+    },
+
+    /* Disable the location filter */
+    disable: function() {
+        if (!this._enabled) {
+            return;
+        }
+
+        // Update buttons
+        this._buttonContainer.removeClass("enabled");
+
+        if (this._enableButton) {
+            this._enableButton.setText(this.options.enableButton.enableText);
+        }
+
+        if (this._adjustButton) {
+            this._adjustButton.remove();
+        }
+
+        // Remove event listener
+        this._map.off("move", this._moveHandler);
+
+        // Remove rectangle layer from map
+        this._map.removeLayer(this._layer);
+
+        this._enabled = false;
+
+        // Call the disabled callback
+        this._callCallback("onDisabled");
+    },
+
+    /* Create a button that allows the user to adjust the location
+       filter to the current zoom */
+    _createAdjustButton: function() {
+        var that = this;
+        this._adjustButton = new L.Control.Button({
+            className: "adjust-button",
+            text: this.options.adjustButton.text,
+            
+            onClick: function(event) {
+                that._adjustToMap();
+                that._callCallback("onAdjustToZoomClick");
+            }
+        }).addTo(this._buttonContainer);
+    },
+
+    /* Create the location filter button container and the button that
+       toggles the location filter */
+    _initializeButtonContainer: function() {
+        var that = this;
+        this._buttonContainer = new L.Control.ButtonContainer({className: "location-filter button-container"});
+
+        if (this.options.enableButton) {
+            this._enableButton = new L.Control.Button({
+                className: "enable-button",
+                text: this.options.enableButton.enableText,
+
+                onClick: function(event) {
+                    if (!that._enabled) {
+                        // Enable the location filter
+                        that.enable();
+                        that._callCallback("onEnableClick");
+                    } else {
+                        // Disable the location filter
+                        that.disable();
+                        that._callCallback("onDisableClick");
+                    }
+                }
+            }).addTo(this._buttonContainer);
+        }
+
+        this._buttonContainer.addTo(this._map);
+    }
+});
\ No newline at end of file