]> git.openstreetmap.org Git - rails.git/commitdiff
Merge branch 'master' into openstreetbugs
authorTom Hughes <tom@compton.nu>
Wed, 22 Aug 2012 19:52:08 +0000 (20:52 +0100)
committerTom Hughes <tom@compton.nu>
Wed, 22 Aug 2012 19:52:08 +0000 (20:52 +0100)
Conflicts:
Gemfile.lock
app/views/browse/_map.html.erb
app/views/user/view.html.erb
config/locales/en.yml
config/openlayers.cfg
db/structure.sql
vendor/assets/openlayers/OpenLayers.js

60 files changed:
Gemfile
Gemfile.lock
app/assets/images/closed_note_marker.png [new file with mode: 0644]
app/assets/images/new_note_marker.png [new file with mode: 0644]
app/assets/images/open_note_marker.png [new file with mode: 0644]
app/assets/javascripts/notes.js.erb [new file with mode: 0644]
app/assets/stylesheets/common.css.scss
app/assets/stylesheets/large.css
app/assets/stylesheets/notes.css [new file with mode: 0644]
app/controllers/browse_controller.rb
app/controllers/notes_controller.rb [new file with mode: 0644]
app/helpers/application_helper.rb
app/models/note.rb [new file with mode: 0644]
app/models/note_comment.rb [new file with mode: 0644]
app/models/notifier.rb
app/models/user.rb
app/views/browse/_map.html.erb
app/views/browse/note.html.erb [new file with mode: 0644]
app/views/notes/_description.html.erb [new file with mode: 0644]
app/views/notes/_note.gpx.builder [new file with mode: 0644]
app/views/notes/_note.json.jsonify [new file with mode: 0644]
app/views/notes/_note.rss.builder [new file with mode: 0644]
app/views/notes/_note.xml.builder [new file with mode: 0644]
app/views/notes/_notes_paging_nav.html.erb [new file with mode: 0644]
app/views/notes/_user.html.erb [new file with mode: 0644]
app/views/notes/feed.rss.builder [new file with mode: 0644]
app/views/notes/index.gpx.builder [new file with mode: 0644]
app/views/notes/index.json.jsonify [new file with mode: 0644]
app/views/notes/index.rss.builder [new file with mode: 0644]
app/views/notes/index.xml.builder [new file with mode: 0644]
app/views/notes/mine.html.erb [new file with mode: 0644]
app/views/notes/show.gpx.builder [new file with mode: 0644]
app/views/notes/show.json.jsonify [new file with mode: 0644]
app/views/notes/show.rss.builder [new file with mode: 0644]
app/views/notes/show.xml.builder [new file with mode: 0644]
app/views/notifier/note_comment_notification.html.erb [new file with mode: 0644]
app/views/site/index.html.erb
app/views/user/view.html.erb
config/example.application.yml
config/initializers/mime_types.rb
config/locales/de.yml
config/locales/en.yml
config/openlayers.cfg
config/routes.rb
db/migrate/053_add_map_bug_tables.rb [new file with mode: 0644]
db/migrate/054_refactor_map_bug_tables.rb [new file with mode: 0644]
db/migrate/055_change_map_bug_comment_type.rb [new file with mode: 0644]
db/migrate/056_add_date_closed.rb [new file with mode: 0644]
db/migrate/057_add_map_bug_comment_event.rb [new file with mode: 0644]
db/migrate/20110508145337_cleanup_bug_tables.rb [new file with mode: 0644]
db/migrate/20110521142405_rename_bugs_to_notes.rb [new file with mode: 0644]
db/structure.sql
lib/bounding_box.rb
lib/geo_record.rb
lib/migrate.rb
test/fixtures/note_comments.yml [new file with mode: 0644]
test/fixtures/notes.yml [new file with mode: 0644]
test/functional/browse_controller_test.rb
test/functional/notes_controller_test.rb [new file with mode: 0644]
vendor/assets/openlayers/OpenLayers.js

diff --git a/Gemfile b/Gemfile
index f4be08961e9c17e969674b926ea8196b6e134cf3..25b10fe7b09c3895944fae03c49849846bc8bc0d 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -24,6 +24,7 @@ gem 'composite_primary_keys', '>= 5.0.8'
 gem 'http_accept_language', '>= 1.0.2'
 gem 'paperclip', '~> 2.0'
 gem 'deadlock_retry', '>= 1.2.0'
+gem 'jsonify-rails'
 
 # We need ruby-openid 2.2.0 or later for ruby 1.9 support
 gem 'ruby-openid', '>= 2.2.0'
index 6f2e73d431adfcadd9b4255749c9068492fd1c63..158694564993cb1f7b9232757d8171d60d33f6fc 100644 (file)
@@ -58,10 +58,15 @@ GEM
     i18n (0.6.0)
     iconv (0.1)
     journey (1.0.4)
-    jquery-rails (2.1.0)
+    jquery-rails (2.1.1)
       railties (>= 3.1.0, < 5.0)
       thor (~> 0.14)
     json (1.7.5)
+    jsonify (0.3.1)
+      multi_json (~> 1.0)
+    jsonify-rails (0.3.2)
+      actionpack
+      jsonify (< 0.4.0)
     jwt (0.1.5)
       multi_json (>= 1.0)
     libv8 (3.3.10.4)
@@ -171,6 +176,7 @@ DEPENDENCIES
   httpclient
   iconv
   jquery-rails
+  jsonify-rails
   libxml-ruby (>= 2.0.5)
   memcached (>= 1.4.1)
   minitest
diff --git a/app/assets/images/closed_note_marker.png b/app/assets/images/closed_note_marker.png
new file mode 100644 (file)
index 0000000..bf6d6bb
Binary files /dev/null and b/app/assets/images/closed_note_marker.png differ
diff --git a/app/assets/images/new_note_marker.png b/app/assets/images/new_note_marker.png
new file mode 100644 (file)
index 0000000..671cf42
Binary files /dev/null and b/app/assets/images/new_note_marker.png differ
diff --git a/app/assets/images/open_note_marker.png b/app/assets/images/open_note_marker.png
new file mode 100644 (file)
index 0000000..a580316
Binary files /dev/null and b/app/assets/images/open_note_marker.png differ
diff --git a/app/assets/javascripts/notes.js.erb b/app/assets/javascripts/notes.js.erb
new file mode 100644 (file)
index 0000000..36eabea
--- /dev/null
@@ -0,0 +1,156 @@
+function addNoteLayer(map, notesUrl, newNoteControls, minZoom) {
+    var newNotes;
+
+    var noteCallback = function (scope, response) {
+        for (var f = 0; f < response.features.length; f++) {
+            var feature = response.features[f];
+        }
+    };
+
+    var saveNewNotes = function (o) {
+        var layer = o.object;
+        newNotes = layer.getFeaturesByAttribute("status", "new")
+        layer.removeFeatures(newNotes, { silent: true });
+    };
+
+    var restoreNewNotes = function (o) {
+        var layer = o.object;
+        layer.addFeatures(newNotes);
+        newNotes = undefined;
+    };
+
+    var noteSelected = function (o) {
+        var feature = o.feature;
+        var location = feature.geometry.getBounds().getCenterLonLat();
+
+        feature.popup = new OpenLayers.Popup.FramedCloud(
+            feature.attributes.id, location, null,
+            "<p>" + feature.attributes.id + "</p>",
+            null, 
+            feature.attributes.status !== "new",
+            function (e) { map.noteSelector.unselect(feature) }
+        );
+
+        map.addPopup(feature.popup);
+        //    feature.popup.show();
+    };
+
+    var noteUnselected = function (o) {
+        var feature = o.feature;
+
+        map.removePopup(feature.popup);
+
+        delete feature.popup;
+    };
+
+    var allowNoteReports = function () { 
+        if (map.getZoom() > minZoom) {
+            newNoteControls.show();
+        } else {
+            newNoteControls.hide();
+        }
+    };
+
+    var addNote = function () {
+        var lonlat = map.getCenter();
+        var layer = map.noteLayer;
+        var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
+        var feature = new OpenLayers.Feature.Vector(geometry, {
+            status: "new"
+        });
+
+        layer.addFeatures(feature);
+        map.noteSelector.unselectAll();
+        map.noteSelector.select(feature);
+        map.noteMover.activate();
+        map.noteLayer.setVisibility(true);
+    };
+
+    map.noteLayer = new OpenLayers.Layer.Vector("Notes", {
+        visibility: false,
+        projection: new OpenLayers.Projection("EPSG:4326"),
+        styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({
+            graphicWidth: 22,
+            graphicHeight: 22,
+            graphicOpacity: 0.7,
+            graphicXOffset: -11,
+            graphicYOffset: -11
+        }, {
+            rules: [
+                new OpenLayers.Rule({
+                    filter: new OpenLayers.Filter.Comparison({
+                        type: OpenLayers.Filter.Comparison.EQUAL_TO,
+                        property: "status",
+                        value: "new"
+                    }),
+                    symbolizer: {
+                        externalGraphic: "<%= image_path 'new_note_marker.png' %>"
+                    }
+                }),
+                new OpenLayers.Rule({
+                    filter: new OpenLayers.Filter.Comparison({
+                        type: OpenLayers.Filter.Comparison.EQUAL_TO,
+                        property: "status",
+                        value: "open"
+                    }),
+                    symbolizer: {
+                        externalGraphic: "<%= image_path 'open_note_marker.png' %>"
+                    }
+                }),
+                new OpenLayers.Rule({
+                    filter: new OpenLayers.Filter.Comparison({
+                        type: OpenLayers.Filter.Comparison.EQUAL_TO,
+                        property: "status",
+                        value: "closed"
+                    }),
+                    symbolizer: {
+                        externalGraphic: "<%= image_path 'closed_note_marker.png' %>"
+                    }
+                })
+            ]
+        })),
+        strategies: [
+            new OpenLayers.Strategy.BBOX()
+        ],
+        protocol: new OpenLayers.Protocol.HTTP({
+            url: notesUrl,
+            format: new OpenLayers.Format.GeoJSON(),
+            callback: noteCallback
+        })
+    });
+
+    map.noteLayer.events.register("beforefeaturesremoved", map, saveNewNotes);
+    map.noteLayer.events.register("featuresremoved", map, restoreNewNotes);
+    map.noteLayer.events.register("featureselected", map, noteSelected);
+    map.noteLayer.events.register("featureunselected", map, noteUnselected);
+
+    map.addLayer(map.noteLayer);
+
+    map.noteSelector = new OpenLayers.Control.SelectFeature(map.noteLayer, {
+        autoActivate: true
+    });
+
+    map.addControl(map.noteSelector);
+
+    map.noteMover = new OpenLayers.Control.DragFeature(map.noteLayer, {
+        onDrag: function (feature, pixel) {
+            feature.popup.lonlat = feature.geometry.getBounds().getCenterLonLat();
+            feature.popup.updatePosition();
+        },
+        featureCallbacks: {
+            over: function (feature) {
+                if (feature.attributes.status === "new") {
+                    map.noteMover.overFeature.apply(map.noteMover, [feature]);
+                }
+            }
+        }
+    });
+
+    map.addControl(map.noteMover);
+
+    newNoteControls.click(addNote);
+
+    map.events.register("zoomend", map, allowNoteReports);
+
+    return map.noteLayer;
+}
index c32144128838d6f380ab9f7a9aa4491fbc840126..f2f16ec85ec93e70a05bbada359a9f38229fcee0 100644 (file)
@@ -718,6 +718,23 @@ table.browse_details th {
   white-space: nowrap;
 }
 
+td.browse_comments {
+  padding: 0px;
+}
+
+td.browse_comments table {
+  border-collapse: collapse;
+}
+
+td.browse_comments table td {
+  padding-bottom: 10px;
+}
+
+td.browse_comments table td span.by {
+  font-size: small;
+  color: #999999;
+}
+
 #browse_map {
   width: 250px;
 }
index 05da4445f8cd75b5c459f4418158d525be187c22..51e9999249b60e92bb41bb5d8aed56fc7f646eef 100644 (file)
@@ -17,3 +17,9 @@
 .olControlZoom {
   display: none;
 }
+
+/* Rules for map bug reporting */
+
+#reportbuganchor { 
+  font-size: 150%;
+}
diff --git a/app/assets/stylesheets/notes.css b/app/assets/stylesheets/notes.css
new file mode 100644 (file)
index 0000000..ee6198b
--- /dev/null
@@ -0,0 +1,42 @@
+.olPopupFramedCloudNotes dl {
+    margin: 0px;
+    padding: 0px;
+}
+
+.olPopupFramedCloudNotes dt {
+    margin: 0px;
+    padding: 0px;
+    font-weight: bold;
+    float: left;
+    clear: left;
+}
+
+.olPopupFramedCloudNotes dt:after {
+    content: ": ";
+}
+
+.olPopupFramedCloudNotes dt {
+    margin-right: 1ex;
+}
+
+.olPopupFramedCloudNotes dd {
+    margin: 0px;
+    padding: 0px;
+}
+
+.olPopupFramedCloudNotes ul.buttons {
+    list-style-type: none;
+    padding: 0px;
+    margin: 0px;
+}
+
+.olPopupFramedCloudNotes ul.buttons li {
+    display: inline;
+    margin: 0px;
+    padding: 0px;
+}
+
+.olPopupFramedCloudNotes h3 {
+    font-size: 1.2em;
+    margin: 0.2em 0em 0.7em 0em;
+}
index f423c6388753ddd80bc65e802d7e4a288640bac6..73f0940d869b6cf6ccfbdeff483affd78c577e3c 100644 (file)
@@ -84,4 +84,13 @@ class BrowseController < ApplicationController
   rescue ActiveRecord::RecordNotFound
     render :action => "not_found", :status => :not_found
   end
+
+  def note
+    @type = "note"
+    @note = Note.find(params[:id])
+    @next = Note.find(:first, :order => "id ASC", :conditions => [ "status != 'hidden' AND id > :id", { :id => @note.id }] )
+    @prev = Note.find(:first, :order => "id DESC", :conditions => [ "status != 'hidden' AND id < :id", { :id => @note.id }] )
+  rescue ActiveRecord::RecordNotFound
+    render :action => "not_found", :status => :not_found
+  end
 end
diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb
new file mode 100644 (file)
index 0000000..fe3615d
--- /dev/null
@@ -0,0 +1,330 @@
+class NotesController < ApplicationController
+
+  layout 'site', :only => [:mine]
+
+  before_filter :check_api_readable
+  before_filter :authorize_web, :only => [:create, :close, :update, :delete, :mine]
+  before_filter :check_api_writable, :only => [:create, :close, :update, :delete]
+  before_filter :set_locale, :only => [:mine]
+  after_filter :compress_output
+  around_filter :api_call_handle_error, :api_call_timeout
+
+  ##
+  # Return a list of notes in a given area
+  def index
+    # Figure out the bbox - we prefer a bbox argument but also
+    # support the old, deprecated, method with four arguments
+    if params[:bbox]
+      bbox = BoundingBox.from_bbox_params(params)
+    else
+      raise OSM::APIBadUserInput.new("No l was given") unless params[:l]
+      raise OSM::APIBadUserInput.new("No r was given") unless params[:r]
+      raise OSM::APIBadUserInput.new("No b was given") unless params[:b]
+      raise OSM::APIBadUserInput.new("No t was given") unless params[:t]
+
+      bbox = BoundingBox.from_lrbt_params(params)
+    end
+
+    # Get any conditions that need to be applied
+    notes = closed_condition(Note.scoped)
+
+    # Check that the boundaries are valid
+    bbox.check_boundaries
+
+    # Check the the bounding box is not too big
+    bbox.check_size(MAX_NOTE_REQUEST_AREA)
+
+    # Find the notes we want to return
+    @notes = notes.bbox(bbox).order("updated_at DESC").limit(result_limit).preload(:comments)
+
+    # Render the result
+    respond_to do |format|
+      format.rss
+      format.xml
+      format.json
+      format.gpx
+    end
+  end
+
+  ##
+  # Create a new note
+  def create
+    # Check the arguments are sane
+    raise OSM::APIBadUserInput.new("No lat was given") unless params[:lat]
+    raise OSM::APIBadUserInput.new("No lon was given") unless params[:lon]
+    raise OSM::APIBadUserInput.new("No text was given") unless params[:text]
+
+    # Extract the arguments
+    lon = params[:lon].to_f
+    lat = params[:lat].to_f
+    comment = params[:text]
+    name = params[:name]
+
+    # Include in a transaction to ensure that there is always a note_comment for every note
+    Note.transaction do
+      # Create the note
+      @note = Note.create(:lat => lat, :lon => lon)
+      raise OSM::APIBadUserInput.new("The note is outside this world") unless @note.in_world?
+
+      #TODO: move this into a helper function
+      begin
+        url = "http://nominatim.openstreetmap.org/reverse?lat=" + lat.to_s + "&lon=" + lon.to_s + "&zoom=16" 
+        response = REXML::Document.new(Net::HTTP.get(URI.parse(url))) 
+               
+        if result = response.get_text("reversegeocode/result") 
+          @note.nearby_place = result.to_s 
+        else 
+          @note.nearby_place = "unknown"
+        end
+      rescue Exception => err
+        @note.nearby_place = "unknown"
+      end
+
+      # Save the note
+      @note.save!
+
+      # Add a comment to the note
+      add_comment(@note, comment, name, "opened")
+    end
+
+    # Send an OK response
+    render_ok
+  end
+
+  ##
+  # Add a comment to an existing note
+  def comment
+    # Check the arguments are sane
+    raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
+    raise OSM::APIBadUserInput.new("No text was given") unless params[:text]
+
+    # Extract the arguments
+    id = params[:id].to_i
+    comment = params[:text]
+    name = params[:name] or "NoName"
+
+    # Find the note and check it is valid
+    note = Note.find(id)
+    raise OSM::APINotFoundError unless note
+    raise OSM::APIAlreadyDeletedError unless note.visible?
+
+    # Add a comment to the note
+    Note.transaction do
+      add_comment(note, comment, name, "commented")
+    end
+
+    # Send an OK response
+    render_ok
+  end
+
+  ##
+  # Close a note
+  def close
+    # Check the arguments are sane
+    raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
+
+    # Extract the arguments
+    id = params[:id].to_i
+    name = params[:name]
+
+    # Find the note and check it is valid
+    note = Note.find_by_id(id)
+    raise OSM::APINotFoundError unless note
+    raise OSM::APIAlreadyDeletedError unless note.visible?
+
+    # Close the note and add a comment
+    Note.transaction do
+      note.close
+
+      add_comment(note, nil, name, "closed")
+    end
+
+    # Send an OK response
+    render_ok
+  end 
+
+  ##
+  # Get a feed of recent notes and comments
+  def feed
+    # Get any conditions that need to be applied
+    notes = closed_condition(Note.scoped)
+
+    # Process any bbox
+    if params[:bbox]
+      bbox = BoundingBox.from_bbox_params(params)
+
+      bbox.check_boundaries
+      bbox.check_size(MAX_NOTE_REQUEST_AREA)
+
+      notes = notes.bbox(bbox)
+    end
+
+    # Find the comments we want to return
+    @comments = NoteComment.where(:note_id => notes).order("created_at DESC").limit(result_limit).preload(:note)
+
+    # Render the result
+    respond_to do |format|
+      format.rss
+    end
+  end
+
+  ##
+  # Read a note
+  def show
+    # Check the arguments are sane
+    raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
+
+    # Find the note and check it is valid
+    @note = Note.find(params[:id])
+    raise OSM::APINotFoundError unless @note
+    raise OSM::APIAlreadyDeletedError unless @note.visible?
+    
+    # Render the result
+    respond_to do |format|
+      format.xml
+      format.rss
+      format.json
+      format.gpx
+    end
+  end
+
+  ##
+  # Delete (hide) a note
+  def destroy
+    # Check the arguments are sane
+    raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
+
+    # Extract the arguments
+    id = params[:id].to_i
+    name = params[:name]
+
+    # Find the note and check it is valid
+    note = Note.find(id)
+    raise OSM::APINotFoundError unless note
+    raise OSM::APIAlreadyDeletedError unless note.visible?
+
+    # Mark the note as hidden
+    Note.transaction do
+      note.status = "hidden"
+      note.save
+
+      add_comment(note, nil, name, "hidden")
+    end
+
+    # Render the result
+    render :text => "ok\n", :content_type => "text/html" 
+  end
+
+  ##
+  # Return a list of notes matching a given string
+  def search
+    # Check the arguments are sane
+    raise OSM::APIBadUserInput.new("No query string was given") unless params[:q]
+
+    # Get any conditions that need to be applied
+    @notes = closed_condition(Note.scoped)
+    @notes = @notes.joins(:comments).where("note_comments.body ~ ?", params[:q])
+
+    # Find the notes we want to return
+    @notes = @notes.order("updated_at DESC").limit(result_limit).preload(:comments)
+
+    # Render the result
+    respond_to do |format|
+      format.rss { render :action => :index }
+      format.xml { render :action => :index }
+      format.json { render :action => :index }
+      format.gpx { render :action => :index }
+    end
+  end
+
+  ##
+  # Display a list of notes by a specified user
+  def mine
+    if params[:display_name] 
+      if @this_user = User.active.find_by_display_name(params[:display_name])
+        @title =  t 'note.mine.title', :user => @this_user.display_name 
+        @heading =  t 'note.mine.heading', :user => @this_user.display_name 
+        @description = t 'note.mine.description', :user => render_to_string(:partial => "user", :object => @this_user)
+        @page = (params[:page] || 1).to_i 
+        @page_size = 10
+        @notes = @this_user.notes.order("updated_at DESC").offset((@page - 1) * @page_size).limit(@page_size).preload(:comments => :author)
+      else
+        @title = t 'user.no_such_user.title' 
+        @not_found_user = params[:display_name] 
+
+        render :template => 'user/no_such_user', :status => :not_found 
+      end 
+    end
+  end
+
+private 
+  #------------------------------------------------------------ 
+  # utility functions below. 
+  #------------------------------------------------------------   
+  ##
+  # Render an OK response
+  def render_ok
+    if params[:format] == "js"
+      render :text => "osbResponse();", :content_type => "text/javascript" 
+    else
+      render :text => "ok " + @note.id.to_s + "\n", :content_type => "text/plain" if @note
+      render :text => "ok\n", :content_type => "text/plain" unless @note
+    end
+  end
+
+  ##
+  # Get the maximum number of results to return
+  def result_limit
+    if params[:limit] and params[:limit].to_i > 0 and params[:limit].to_i < 10000
+      params[:limit].to_i
+    else
+      100
+    end
+  end
+
+  ##
+  # Generate a condition to choose which bugs we want based
+  # on their status and the user's request parameters
+  def closed_condition(notes)
+    if params[:closed]
+      closed_since = params[:closed].to_i
+    else
+      closed_since = 7
+    end
+       
+    if closed_since < 0
+      notes = notes.where("status != 'hidden'")
+    elsif closed_since > 0
+      notes = notes.where("(status = 'open' OR (status = 'closed' AND closed_at > '#{Time.now - closed_since.days}'))")
+    else
+      notes = notes.where("status = 'open'")
+    end
+
+    return notes
+  end
+
+  ##
+  # Add a comment to a note
+  def add_comment(note, text, name, event)
+    name = "NoName" if name.nil?
+
+    attributes = { :visible => true, :event => event, :body => text }
+
+    if @user  
+      attributes[:author_id] = @user.id
+      attributes[:author_name] = @user.display_name
+    else  
+      attributes[:author_ip] = request.remote_ip
+      attributes[:author_name] = name + " (a)"
+    end
+
+    note.comments.create(attributes, :without_protection => true)
+
+    note.comments.map { |c| c.author }.uniq.each do |user|
+      if user and user != @user
+        Notifier.deliver_note_comment_notification(comment, user)
+      end
+    end
+  end
+end
index 3213c5e4c41beac7312ca3a26472c5167acd60ea..e6a1e58e688721a0272c4f0a489424c5a86dc7c3 100644 (file)
@@ -107,6 +107,18 @@ module ApplicationHelper
     end
   end
 
+  def friendly_date(date)
+    content_tag(:span, time_ago_in_words(date), :title => l(date, :format => :friendly))
+  end
+
+  def note_author(object, link_options = {})
+    if object.author.nil?
+      h(object.author_name)
+    else
+      link_to h(object.author_name), link_options.merge({:controller => "user", :action => "view", :display_name => object.author_name})
+    end
+  end
+
 private
 
   def javascript_strings_for_key(key)
diff --git a/app/models/note.rb b/app/models/note.rb
new file mode 100644 (file)
index 0000000..c32b167
--- /dev/null
@@ -0,0 +1,81 @@
+class Note < ActiveRecord::Base
+  include GeoRecord
+
+  has_many :comments, :class_name => "NoteComment",
+                      :foreign_key => :note_id,
+                      :order => :created_at,
+                      :conditions => { :visible => true }
+
+  validates_presence_of :id, :on => :update
+  validates_uniqueness_of :id
+  validates_numericality_of :latitude, :only_integer => true
+  validates_numericality_of :longitude, :only_integer => true
+  validates_presence_of :closed_at if :status == "closed"
+  validates_inclusion_of :status, :in => ["open", "closed", "hidden"]
+  validate :validate_position
+
+  attr_accessible :lat, :lon
+
+  after_initialize :set_defaults
+
+  # Sanity check the latitude and longitude and add an error if it's broken
+  def validate_position
+    errors.add(:base, "Note is not in the world") unless in_world?
+  end
+
+  # Close a note
+  def close
+    self.status = "closed"
+    self.closed_at = Time.now.getutc
+    self.save
+  end
+
+  # Return a flattened version of the comments for a note
+  def flatten_comment(separator_char, upto_timestamp = :nil)
+    resp = ""
+    comment_no = 1
+    self.comments.each do |comment|
+      next if upto_timestamp != :nil and comment.created_at > upto_timestamp
+      resp += (comment_no == 1 ? "" : separator_char)
+      resp += comment.body if comment.body
+      resp += " [ " 
+      resp += comment.author_name if comment.author_name
+      resp += " " + comment.created_at.to_s + " ]"
+      comment_no += 1
+    end
+
+    return resp
+  end
+
+  # Check if a note is visible
+  def visible?
+    return status != "hidden"
+  end
+
+  # Return the author object, derived from the first comment
+  def author
+    self.comments.first.author
+  end
+
+  # Return the author IP address, derived from the first comment
+  def author_ip
+    self.comments.first.author_ip
+  end
+
+  # Return the author id, derived from the first comment
+  def author_id
+    self.comments.first.author_id
+  end
+
+  # Return the author name, derived from the first comment
+  def author_name
+    self.comments.first.author_name
+  end
+
+private
+
+  # Fill in default values for new notes
+  def set_defaults
+    self.status = "open" unless self.attribute_present?(:status)
+  end
+end
diff --git a/app/models/note_comment.rb b/app/models/note_comment.rb
new file mode 100644 (file)
index 0000000..bcbcf79
--- /dev/null
@@ -0,0 +1,21 @@
+class NoteComment < ActiveRecord::Base
+  belongs_to :note, :foreign_key => :note_id
+  belongs_to :author, :class_name => "User", :foreign_key => :author_id
+
+  validates_presence_of :id, :on => :update
+  validates_uniqueness_of :id
+  validates_presence_of :note_id
+  validates_associated :note
+  validates_presence_of :visible
+  validates_associated :author
+  validates_inclusion_of :event, :in => [ "opened", "closed", "reopened", "commented", "hidden" ]
+
+  # Return the author name
+  def author_name
+    if self.author_id.nil?
+      self.read_attribute(:author_name)
+    else
+      self.author.display_name
+    end
+  end
+end
index 343c3db2292fd9fd7625abe9b87889671e6d6f50..2fb00c96f8f52255e1c1e5f8ff4f12baebca76ad 100644 (file)
@@ -114,6 +114,22 @@ class Notifier < ActionMailer::Base
          :subject => I18n.t('notifier.friend_notification.subject', :user => friend.befriender.display_name, :locale => @locale)
   end
 
+  def note_comment_notification(comment, recipient)
+    common_headers recipient
+    owner = (recipient == comment.note.author);
+    subject I18n.t('notifier.note_plain.subject_own', :commenter => comment.author_name) if owner
+    subject I18n.t('notifier.note_plain.subject_other', :commenter => comment.author_name) unless owner
+
+    body :nodeurl => url_for(:host => SERVER_URL,
+                             :controller => "browse",
+                             :action => "note",
+                             :id => comment.note_id),
+         :place => comment.note.nearby_place,
+         :comment => comment.body,
+         :owner => owner,
+         :commenter => comment.author_name
+  end
+
 private
 
   def from_address(name, type, id, digest)
index 68537e749fa5c794bba5ef5f13a49911f563e829..e126adb98401ea656041a9a3a58f9599d9bbffeb 100644 (file)
@@ -12,6 +12,8 @@ class User < ActiveRecord::Base
   has_many :tokens, :class_name => "UserToken"
   has_many :preferences, :class_name => "UserPreference"
   has_many :changesets, :order => 'created_at DESC'
+  has_many :note_comments, :foreign_key => :author_id
+  has_many :notes, :through => :note_comments
 
   has_many :client_applications
   has_many :oauth_tokens, :class_name => "OauthToken", :order => "authorized_at desc", :include => [:client_application]
index 2c29a0d008999a2fc7491ef915a328b1602e9551..d8ca30a15a220550da4fbe2d0616b036d2b77d4b 100644 (file)
@@ -5,14 +5,18 @@
 </iframe>
 
 <div id="browse_map">
-  <% if map.instance_of? Changeset or (map.instance_of? Node and map.version > 1) or map.visible %>
+  <% if map.instance_of? Changeset or (map.instance_of? Node and map.version > 1) or map.visible? %>
   <div id="small_map">
   </div>
   <span id="loading"><%= t 'browse.map.loading' %></span>
+  <% if map.instance_of? Note -%>
+  <%= link_to(t("browse.map.larger.area"), { :controller => :site, :action => :index, :notes => "yes" }, { :id => "area_larger_map", :class => "geolink bbox" }) %>
+  <% else -%>
   <%= link_to(t("browse.map.larger.area"), { :controller => :site, :action => :index, :box => "yes" }, { :id => "area_larger_map", :class => "geolink bbox" }) %>
+  <% end -%>
   <br />
   <%= link_to(h(t("browse.map.edit.area")) + content_tag(:span, "▾", :class => "menuicon"), { :controller => :site, :action => :edit }, { :id => "area_edit", :class => "geolink bbox" }) %>
-  <% unless map.instance_of? Changeset %>
+  <% unless map.instance_of? Changeset or map.instance_of? Note %>
     <br />
     <%= link_to(t("browse.map.larger." + map.class.to_s.downcase), { :controller => :site, :action => :index }, { :id => "object_larger_map", :class => "geolink object" }) %>
     <br />
@@ -39,7 +43,7 @@
   </ul>
 </div>
 
-<% if map.instance_of? Changeset or (map.instance_of? Node and map.version > 1) or map.visible %>
+<% if map.instance_of? Changeset or (map.instance_of? Node and map.version > 1) or map.visible? %>
   <script type="text/javascript">
     OpenLayers.Lang.setCode("<%= I18n.locale.to_s %>");
 
         });
         <% end -%>
 
-        updatelinks(centre.lon, centre.lat, 16, null, minlon, minlat, maxlon, maxlat);
-      <% else -%>
+        updatelinks(centre.lon, centre.lat, 16, null, minlon, minlat, maxlon, maxlat)
+      <% elsif map.instance_of? Note %>
+        var centre = new OpenLayers.LonLat(<%= map.lon %>, <%= map.lat %>);
+
+        setMapCenter(centre, 16);
+        addMarkerToMap(centre);
+
+        var bbox = getMapExtent();
+
+        $("#loading").hide();
+        $("#browse_map .geolink").show();
+
+        $("#remote_area_edit").click(function (event) {
+          return remoteEditHandler(event, bbox);
+        });
+
+        <% if preferred_editor == "remote" -%>
+        $("#area_edit").click(function (event) {
+          return remoteEditHandler(event, bbox);
+        });
+        <% end -%>
+
+        updatelinks(centre.lon, centre.lat, 16, null, bbox.left, bbox.bottom, bbox.right, bbox.top)
+      <% else %>
         var obj_type = "<%= map.class.name.downcase %>";
         var obj_id = <%= map.id %>;
         var obj_version = <%= map.version %>;
diff --git a/app/views/browse/note.html.erb b/app/views/browse/note.html.erb
new file mode 100644 (file)
index 0000000..59c96c4
--- /dev/null
@@ -0,0 +1,58 @@
+<%= render :partial => "navigation" %>
+
+<h2>
+  <%= image_tag "#{@note.status}_note_marker.png", :alt => @note.status %>
+  <%= t "browse.note.#{@note.status}_title", :note_name => @note.id %>
+</h2>
+
+<%= render :partial => "map", :object => @note %>
+
+<table class="browse_details">
+
+  <tr>
+    <th><%= t "browse.note.opened" %></th>
+    <td><%= t "browse.note.at_by", :when => friendly_date(@note.created_at), :user => note_author(@note) %></td>
+  </tr>  
+
+  <% if @note.status == "closed" %>
+    <tr>
+      <th><%= t "browse.note.closed" %></th>
+      <td><%= t "browse.note.at_by", :when => friendly_date(@note.closed_at), :user => note_author(@note.comments.last) %></td>
+    </tr>  
+  <% elsif @note.comments.length > 1 %>
+    <tr>
+      <th><%= t "browse.note.last_modified" %></th>
+      <td><%= t "browse.note.at_by", :when => friendly_date(@note.updated_at), :user => note_author(@note.comments.last) %></td>
+    </tr>  
+  <% end %>
+
+  <tr>
+    <th><%= t "browse.note.description" %></th>
+    <td><%= h(@note.comments.first.body) %></td>
+  </tr>
+
+  <tr>
+    <th><%= t "browse.node_details.coordinates" %></th>
+    <td><div class="geo"><%= link_to ("<span class='latitude'>#{number_with_delimiter(@note.lat)}</span>, <span class='longitude'>#{number_with_delimiter(@note.lon)}</span>"), {:controller => 'site', :action => 'index', :lat => h(@note.lat), :lon => h(@note.lon), :zoom => "18"} %></div></td>
+  </tr>
+
+  <% if @note.comments.length > 1 %>
+    <tr valign="top">
+      <th><%= t "browse.note.comments" %></th>
+      <td class="browse_comments">
+        <table>
+          <% @note.comments[1..-1].each do |comment| %>
+            <tr>
+              <td>
+                <%= h(comment.body) %>
+                <br />
+                <span class="by"><%= t "browse.note.at_by", :when => friendly_date(comment.created_at), :user => note_author(comment) %></span>
+              </td>
+            </tr>
+          <% end %>
+        </table>
+      </td>
+    </tr>
+  <% end %>
+
+</table>
diff --git a/app/views/notes/_description.html.erb b/app/views/notes/_description.html.erb
new file mode 100644 (file)
index 0000000..596d632
--- /dev/null
@@ -0,0 +1,8 @@
+<div>
+  <% description.comments.each do |comment| -%>
+  <div class="note-comment" style="margin-top: 5px">
+    <div class="note-comment-description" style="font-size: smaller; color: #999999"><%= t "note.description.#{comment.event}_at_by", :when => friendly_date(comment.created_at), :user => note_author(comment, :only_path => false) %></div>
+    <div class="note-comment-text"><%= comment.body %></div>
+  </div>
+  <% end -%>
+</div>
diff --git a/app/views/notes/_note.gpx.builder b/app/views/notes/_note.gpx.builder
new file mode 100644 (file)
index 0000000..4b9aa98
--- /dev/null
@@ -0,0 +1,15 @@
+xml.wpt("lon" => note.lon, "lat" => note.lat) do
+  xml.desc do
+    xml.cdata! render(:partial => "description", :object => note, :formats => [ :html ])
+  end
+
+  xml.extension do
+    if note.status = "open"
+      xml.closed "0"
+    else
+      xml.closed "1"
+    end
+
+    xml.id note.id
+  end
+end
diff --git a/app/views/notes/_note.json.jsonify b/app/views/notes/_note.json.jsonify
new file mode 100644 (file)
index 0000000..ba9ccd9
--- /dev/null
@@ -0,0 +1,22 @@
+json.type "Feature"
+
+json.geometry do
+  json.type "Point"
+  json.coordinates [ note.lon, note.lat ]              
+end
+
+json.properties do
+  json.id note.id
+  json.date_created note.created_at
+  json.nearby  note.nearby_place
+  json.status note.status
+  json.closed_at note.closed_at if note.status == "closed"
+
+  json.comments(note.comments) do |comment|
+    json.date comment.created_at
+    json.uid comment.author_id unless comment.author_id.nil?
+    json.user comment.author_name
+    json.action comment.event
+    json.text comment.body unless comment.body.nil?
+  end
+end
diff --git a/app/views/notes/_note.rss.builder b/app/views/notes/_note.rss.builder
new file mode 100644 (file)
index 0000000..90d5757
--- /dev/null
@@ -0,0 +1,18 @@
+xml.item do
+  if note.status == "closed"
+    xml.title t('note.rss.closed', :place => note.nearby_place)        
+  elsif note.comments.length > 1
+    xml.title t('note.rss.comment', :place => note.nearby_place)
+  else
+    xml.title t('note.rss.new', :place => note.nearby_place)
+  end
+
+  xml.link url_for(:controller => "browse", :action => "note", :id => note.id, :only_path => false)
+  xml.guid url_for(:controller => "note", :action => "read", :id => note.id, :only_path => false)
+  xml.description render(:partial => "description", :object => note, :formats => [ :html ])
+  xml.author note.author_name
+  xml.pubDate note.updated_at.to_s(:rfc822)
+  xml.geo :lat, note.lat
+  xml.geo :long, note.lon
+  xml.georss :point, "#{note.lat} #{note.lon}"
+end
diff --git a/app/views/notes/_note.xml.builder b/app/views/notes/_note.xml.builder
new file mode 100644 (file)
index 0000000..2a2b2ff
--- /dev/null
@@ -0,0 +1,21 @@
+xml.note("lon" => note.lon, "lat" => note.lat) do
+  xml.id note.id
+  xml.date_created note.created_at
+  xml.nearby note.nearby_place
+  xml.status note.status
+
+  if note.status == "closed"
+    xml.date_closed note.closed_at
+  end
+
+  xml.comments do
+    note.comments.each do |comment|
+      xml.comment do
+        xml.date comment.created_at
+        xml.uid comment.author_id unless comment.author_id.nil?
+        xml.user comment.author_name
+        xml.text comment.body
+      end      
+    end
+  end
+end
diff --git a/app/views/notes/_notes_paging_nav.html.erb b/app/views/notes/_notes_paging_nav.html.erb
new file mode 100644 (file)
index 0000000..108cbb3
--- /dev/null
@@ -0,0 +1,17 @@
+<p>
+
+<% if @page > 1 %>
+<%= link_to t('changeset.changeset_paging_nav.previous'), params.merge({ :page => @page - 1 }) %>
+<% else %>
+<%= t('changeset.changeset_paging_nav.previous') %>
+<% end %>
+
+| <%= t('changeset.changeset_paging_nav.showing_page', :page => @page) %> |
+
+<% if @notes.size < @page_size %>
+<%= t('changeset.changeset_paging_nav.next') %>
+<% else %>
+<%= link_to t('changeset.changeset_paging_nav.next'), params.merge({ :page => @page + 1 }) %>
+<% end %>
+
+</p>
diff --git a/app/views/notes/_user.html.erb b/app/views/notes/_user.html.erb
new file mode 100644 (file)
index 0000000..0e95076
--- /dev/null
@@ -0,0 +1 @@
+<%= link_to user.display_name, :controller => "user", :action => "view", :display_name => user.display_name %>
diff --git a/app/views/notes/feed.rss.builder b/app/views/notes/feed.rss.builder
new file mode 100644 (file)
index 0000000..d22d673
--- /dev/null
@@ -0,0 +1,46 @@
+xml.instruct!
+
+xml.rss("version" => "2.0", 
+        "xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
+        "xmlns:georss" => "http://www.georss.org/georss") do
+  xml.channel do
+    xml.title t('note.rss.title')
+    xml.description t('note.rss.description_area', :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon )
+    xml.link url_for(:controller => "site", :action => "index", :only_path => false)
+
+    @comments.each do |comment|
+      xml.item do
+        if comment.event == "closed"
+          xml.title t('note.rss.closed', :place => comment.note.nearby_place)  
+        elsif comment.event == "commented"
+          xml.title t('note.rss.comment', :place => comment.note.nearby_place)
+        elsif comment.event == "opened"
+          xml.title t('note.rss.new', :place => comment.note.nearby_place)
+        else
+          xml.title "unknown event"
+        end
+        
+        xml.link url_for(:controller => "browse", :action => "note", :id => comment.note.id, :only_path => false)
+        xml.guid url_for(:controller => "browse", :action => "note", :id => comment.note.id, :only_path => false)
+
+        description_text = ""
+
+        if comment.event == "commented" and not comment.nil?
+          description_text += "<b>Comment:</b><br>"
+          description_text += htmlize(comment.body)
+          description_text += "<br>"
+        end
+
+        description_text += "<b>Full note:</b><br>"
+        description_text += comment.note.flatten_comment("<br>", comment.created_at)
+
+        xml.description description_text 
+        xml.author comment.author_name
+        xml.pubDate comment.created_at.to_s(:rfc822)
+        xml.geo :lat, comment.note.lat
+        xml.geo :long, comment.note.lon
+        xml.georss :point, "#{comment.note.lat} #{comment.note.lon}"
+      end
+    end
+  end
+end
diff --git a/app/views/notes/index.gpx.builder b/app/views/notes/index.gpx.builder
new file mode 100644 (file)
index 0000000..7a30460
--- /dev/null
@@ -0,0 +1,7 @@
+xml.instruct!
+
+xml.gpx("version" => "1.1", 
+        "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
+        "xsi:schemaLocation" => "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd") do
+  xml << render(:partial => "note", :collection => @notes)
+end
diff --git a/app/views/notes/index.json.jsonify b/app/views/notes/index.json.jsonify
new file mode 100644 (file)
index 0000000..cb75d41
--- /dev/null
@@ -0,0 +1,5 @@
+json.type "FeatureCollection"
+
+json.features(@notes) do |note|
+  json.ingest! render(:partial => "note", :object => note)
+end
diff --git a/app/views/notes/index.rss.builder b/app/views/notes/index.rss.builder
new file mode 100644 (file)
index 0000000..d6ee2bb
--- /dev/null
@@ -0,0 +1,13 @@
+xml.instruct!
+
+xml.rss("version" => "2.0", 
+        "xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
+        "xmlns:georss" => "http://www.georss.org/georss") do
+  xml.channel do
+    xml.title t('note.rss.title')
+    xml.description t('note.rss.description_area', :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon )
+    xml.link url_for(:controller => "site", :action => "index", :only_path => false)
+
+    xml << render(:partial => "note", :collection => @notes)
+  end
+end
diff --git a/app/views/notes/index.xml.builder b/app/views/notes/index.xml.builder
new file mode 100644 (file)
index 0000000..38b239a
--- /dev/null
@@ -0,0 +1,3 @@
+xml.instruct!
+
+xml << render(:partial => "note", :collection => @notes)
diff --git a/app/views/notes/mine.html.erb b/app/views/notes/mine.html.erb
new file mode 100644 (file)
index 0000000..50c15cd
--- /dev/null
@@ -0,0 +1,37 @@
+<h1><%= @heading %></h1>
+<p><%= raw @description %></p>
+
+<%= render :partial => 'notes_paging_nav' %>
+
+<table id="note_list" cellpadding="3">
+  <tr>
+    <th></th>
+    <th><%= t'note.mine.id' %></th>
+    <th><%= t'changeset.changesets.user' %></th>
+    <th><%= t'changeset.changesets.comment' %></th>
+    <th><%= t'changeset.changesets.saved_at' %></th>
+    <th><%= t'note.mine.last_changed' %></th>
+  </tr>
+<% @notes.each do |note| -%>
+  <tr<% if note.author != @user2 %> bgcolor="#EEEEEE"<% end %>>
+    <td>
+      <% if note.status == "closed" %>
+        <%= image_tag("closed_note_marker.png", :alt => 'closed') %>
+      <% else %>
+        <%= image_tag("open_note_marker.png", :alt => 'open') %>
+      <% end %>
+    </td>
+    <td><%= link_to note.id.to_s, :controller => "browse", :action => "note", :id => note.id %></td>
+    <% if note.author.nil? %> 
+      <td> <%= note.author_name %> </td> 
+    <% else %> 
+      <td><%= link_to h(note.author_name), :controller => "user", :action => "view", :display_name => note.author_name %></td>
+    <% end %>
+    <td> <%= htmlize note.comments.first.body  %> </td>        
+    <td><%= l note.created_at %></td>
+    <td><%= l note.updated_at %></td>
+  </tr>
+<% end -%>
+</table>
+
+<%= render :partial => 'notes_paging_nav' %>
diff --git a/app/views/notes/show.gpx.builder b/app/views/notes/show.gpx.builder
new file mode 100644 (file)
index 0000000..e54d772
--- /dev/null
@@ -0,0 +1,7 @@
+xml.instruct!
+
+xml.gpx("version" => "1.1", 
+        "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
+        "xsi:schemaLocation" => "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd") do
+  xml << render(:partial => "note", :object => @note)
+end
diff --git a/app/views/notes/show.json.jsonify b/app/views/notes/show.json.jsonify
new file mode 100644 (file)
index 0000000..3e5685f
--- /dev/null
@@ -0,0 +1 @@
+json.ingest! render(:partial => "note", :object => @note)
diff --git a/app/views/notes/show.rss.builder b/app/views/notes/show.rss.builder
new file mode 100644 (file)
index 0000000..e566ff0
--- /dev/null
@@ -0,0 +1,13 @@
+xml.instruct!
+
+xml.rss("version" => "2.0", 
+        "xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
+        "xmlns:georss" => "http://www.georss.org/georss") do
+  xml.channel do
+    xml.title t('note.rss.title')
+    xml.description t('note.rss.description_item', :id => @note.id)
+    xml.link url_for(:controller => "site", :action => "index", :only_path => false)
+
+    xml << render(:partial => "note", :object => @note)
+  end
+end
diff --git a/app/views/notes/show.xml.builder b/app/views/notes/show.xml.builder
new file mode 100644 (file)
index 0000000..cfb28c2
--- /dev/null
@@ -0,0 +1,3 @@
+xml.instruct!
+
+xml << render(:partial => "note", :object => @note)
diff --git a/app/views/notifier/note_comment_notification.html.erb b/app/views/notifier/note_comment_notification.html.erb
new file mode 100644 (file)
index 0000000..fade148
--- /dev/null
@@ -0,0 +1,15 @@
+<%= t 'notifier.note_plain.greeting' %>
+
+<% if @owner %>
+<%= t 'notifier.note_plain.your_note', :commenter => @commenter, :place => @place %>
+<% else %>
+<%= t 'notifier.note_plain.commented_note', :commenter => @commenter, :place => @place %>
+<% end %>
+
+==
+<%= @comment %>
+==
+
+<%= t 'notifier.note_plain.details', :URL => @noteurl %>
+
+
index b7a9df068c618b042d522b389b3674abf2a50e0e..8aa318a3fb8f9587b46f68058032872076e79995 100644 (file)
@@ -25,6 +25,7 @@
   <div id="permalink">
     <a href="/" id="permalinkanchor" class="geolink llz layers object"><%= t 'site.index.permalink' %></a>
     <a href="/" id="shortlinkanchor"><%= t 'site.index.shortlink' %></a>
+    <a href="#" id="createnoteanchor">Report a problem</a>     
   </div>
 </div>
 
@@ -127,6 +128,7 @@ end
 
 <%= javascript_include_tag 'openlayers.js' %>
 <%= javascript_include_tag 'map.js' %>
+<%= javascript_include_tag 'notes.js' %>
 
 <%= render :partial => 'resize' %>
 
@@ -136,6 +138,10 @@ end
 
   OpenLayers.Lang.setCode("<%= I18n.locale.to_s %>");
 
+  <% if @user %>
+    var loginName = "<%= @user.display_name %>"
+  <% end %>
+
   function mapInit(){
     map = createMap("map");
 
@@ -145,6 +151,12 @@ end
           displayInLayerSwitcher: false
       });
       map.addLayer(map.dataLayer);
+
+      var noteLayer = addNoteLayer(map, "<%= notes_url :format => 'json' %>", $("#createnoteanchor"), 11);
+
+      <% if params[:notes] == "yes" -%>
+      noteLayer.setVisibility(true);
+      <% end -%>
     <% end %>
 
     <% unless object_zoom %>
index da58043045fea3d93776faeb38b3e682b198e336..97811d3a496e8ea4857ac33ea2b3070863a334af 100644 (file)
@@ -11,6 +11,8 @@
     <%= link_to t('user.view.my traces'), :controller => 'trace', :action=>'mine' %>
     <span class='count-number'><%= number_with_delimiter(@user.traces.size) %></span>
     |
+    <%= link_to t('user.view.my notes'), :controller => 'note', :action=> 'mine' %>
+    |
     <%= link_to t('user.view.my diary'), :controller => 'diary_entry', :action => 'list', :display_name => @user.display_name %>
     |
     <%= link_to t('user.view.my comments' ), :controller => 'diary_entry', :action => 'comments', :display_name => @user.display_name %>
@@ -33,6 +35,8 @@
     <%= link_to t('user.view.traces'), :controller => 'trace', :action => 'list', :display_name => @this_user.display_name %>
     <span class='count-number'><%= number_with_delimiter(@this_user.traces.size) %></span>
     |
+    <%= link_to t('user.view.notes'), :controller => 'note', :action=> 'mine' %>
+    |
     <!-- Displaying another user's profile page -->
     <%= link_to t('user.view.send message'), :controller => 'message', :action => 'new', :display_name => @this_user.display_name %>
     |
index cf18ab5577cceed5fc7fe2395246cc0710c6757f..593509297aa0cd095cf2434025392667fb2b2730 100644 (file)
@@ -24,6 +24,8 @@ defaults: &defaults
   max_number_of_nodes: 50000
   # Maximum number of nodes that can be in a way (checked on save)
   max_number_of_way_nodes: 2000
+  # The maximum area you're allowed to request notes from, in square degrees
+  max_note_request_area: 25
   # Zoom level to use for postcode results from the geocoder
   postcode_zoom: 15
   # Zoom level to use for geonames results from the geocoder
index c676f6e46c50aa019fcb728a648bd0998d8ec89e..be247274f290811f0ccf61b7dee2b6a488343bc9 100644 (file)
@@ -4,3 +4,4 @@
 # Mime::Type.register "text/richtext", :rtf
 # Mime::Type.register_alias "text/html", :iphone
 Mime::Type.register "application/x-amf", :amf
+Mime::Type.register "application/gpx+xml", :gpx
index ecfd1974893080286b68e6a9c8a9bd30e0b205c9..48e10aaba580e6fbed42d1281596975efd5f3ea9 100644 (file)
@@ -958,6 +958,22 @@ de:
       history_disabled_tooltip: Reinzoomen um Änderungen für diesen Bereich anzuzeigen
       history_tooltip: Änderungen für diesen Bereich anzeigen
       history_zoom_alert: Du musst näher heranzoomen, um die Chronik zu sehen
+    osb:
+      Fixed Error: Behobener Fehler
+      Unresolved Error: Offener Fehler
+      Description: Beschreibung
+      Comment: Kommentar
+      Has been fixed: Der Fehler wurde bereits behoben. Es kann jedoch bis zu einigen Tagen dauern, bis die Kartenansicht aktualisiert wird.
+      Comment/Close: Kommentieren/Schließen
+      Nickname: Benutzername
+      Add comment: Kommentar hinzufügen
+      Mark as fixed: Als behoben markieren
+      Cancel: Abbrechen
+      Create OpenStreetBug: OpenStreetBug melden
+      Create bug: Bug anlegen
+      Bug description: Fehlerbeschreibung
+      Create: Anlegeeen
+      Permalink: Permalink
   layouts: 
     community: Gemeinschaft
     community_blogs: Blogs
index 34104a7d126b3ec9021e62763f61d91a3e7fe2c8..8bc0992bbee123819217cee197afcadc4307ce9d 100644 (file)
@@ -120,6 +120,8 @@ en:
         next_relation_tooltip: "Next relation"
         prev_changeset_tooltip: "Previous changeset"
         next_changeset_tooltip: "Next changeset"
+        prev_note_tooltip: "Previous note"
+        next_note_tooltip: "Next note"
     changeset_details:
       created_at: "Created at:"
       closed_at: "Closed at:"
@@ -282,6 +284,15 @@ en:
       download_xml: "Download XML"
       view_history: "View history"
       edit: "Edit way"
+    note:
+      open_title: "Unresolved issue: %{note_name}"
+      closed_title: "Resolved issue: %{note_name}"
+      opened: "Opened:"
+      last_modified: "Last modified:"
+      closed: "Closed:"
+      at_by: "%{when} ago by %{user}"
+      description: "Description:"
+      comments: "Comments:"
   changeset:
     changeset_paging_nav:
       showing_page: "Showing page %{page}"
@@ -1213,6 +1224,13 @@ en:
       greeting: "Hi,"
       hopefully_you: "Someone (possibly you) has asked for the password to be reset on this email address's openstreetmap.org account."
       click_the_link: "If this is you, please click the link below to reset your password."
+    note_plain:
+      subject_own: "[OpenStreetMap] %{commenter} has commented on one of your notes"
+      subject_other: "[OpenStreetMap] %{commenter} has commented on a note you are interested in"
+      greeting: "Hi,"
+      your_note: "%{commenter} has left a comment on one of your map notes near %{place}."
+      commented_note: "%{commenter} has left a comment on a map note you have commented on. The note is near %{place}."
+      details: "More details about the note can be found at %{URL}."
   message:
     inbox:
       title: "Inbox"
@@ -1701,6 +1719,7 @@ en:
       new diary entry: new diary entry
       my edits: my edits
       my traces: my traces
+      my notes: my map notes
       my settings: my settings
       my comments: my comments
       oauth settings: oauth settings
@@ -1710,6 +1729,7 @@ en:
       diary: diary
       edits: edits
       traces: traces
+      notes: map notes
       remove as friend: remove as friend
       add as friend: add as friend
       mapper since: "Mapper since:"
@@ -1976,6 +1996,25 @@ en:
       back: "View all blocks"
       revoker: "Revoker:"
       needs_view: "The user needs to log in before this block will be cleared."
+  note:
+    description:
+      opened_at_by: "Created %{when} ago by %{user}"
+      commented_at_by: "Updated %{when} ago by %{user}"
+      closed_at_by: "Resolved %{when} ago by %{user}"
+      reopened_at_by: "Reactivated %{when} ago by %{user}"
+    rss:
+      title: "OpenStreetMap Notes"
+      description_area: "A list of notes, reported, commented on or closed in your area [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]"
+      description_item: "An rss feed for note %{id}"
+      closed: "closed note (near %{place})"
+      new: "new note (near %{place})"
+      comment: "new comment (near %{place})"
+    mine:
+      title: "Notes submitted or commented on by %{user}"
+      heading: "%{user}'s notes"
+      description: "Notes submitted or commented on by %{user}"
+      id: "Id"
+      last_changed: "Last changed"
   javascripts:
     map:
       base:
@@ -1992,6 +2031,26 @@ en:
       history_tooltip: View edits for this area
       history_disabled_tooltip: Zoom in to view edits for this area
       history_zoom_alert: You must zoom in to view edits for this area
+    note:
+      closed: Closed Note
+      open: Open Note
+      details: Details
+      permalink: Permalink
+      description: Description
+      comment: Comment
+      render_warning: This error has been fixed already. However, it might take a couple of days before the map image is updated.
+      update: Update
+      nickname: Nickname
+      login: Login
+      add_comment: Add Comment
+      close: Close
+      cancel: Cancel
+      create: Create Note
+      create_title: Report a problem with the map
+      create_help1: Please drag the marker to the location of the problem
+      create_help2: and descripe it as accurate as possible
+      report: Report Problem
+      edityourself: You can also edit the map directly your self
   redaction:
     edit:
       description: "Description"
index b671faa3e6fc2b816bf2f93469e1f3e55be449f7..1d4f45a56d2058a49dd799ce096a1119338220c6 100644 (file)
@@ -6,6 +6,7 @@
 OpenLayers/Map.js
 OpenLayers/Control/ArgParser.js
 OpenLayers/Control/Attribution.js
+OpenLayers/Control/DragFeature.js
 OpenLayers/Control/DrawFeature.js
 OpenLayers/Control/LayerSwitcher.js
 OpenLayers/Control/Navigation.js
@@ -25,12 +26,15 @@ OpenLayers/Tile.js
 OpenLayers/Tile/Image.js
 OpenLayers/Feature/Vector.js
 OpenLayers/Strategy/Fixed.js
+OpenLayers/Strategy/BBOX.js
 OpenLayers/Protocol/HTTP.js
+OpenLayers/Format/GeoJSON.js
 OpenLayers/Format/OSM.js
 OpenLayers/Geometry/Point.js
 OpenLayers/Geometry/LinearRing.js
 OpenLayers/Handler/Point.js
 OpenLayers/Handler/RegularPolygon.js
+OpenLayers/Protocol/HTTP.js
 OpenLayers/Renderer.js
 OpenLayers/Renderer/Canvas.js
 OpenLayers/Renderer/SVG.js
index 1ef0c31af4fe978de32d5679a0ff64619478277a..6fbe07f8ed108259526dc7b2fdd72c63e0b0c8f4 100644 (file)
@@ -73,11 +73,31 @@ OpenStreetMap::Application.routes.draw do
   match 'api/0.6/gpx/:id/data' => 'trace#api_data', :via => :get
   
   # AMF (ActionScript) API
-
   match 'api/0.6/amf/read' => 'amf#amf_read', :via => :post
   match 'api/0.6/amf/write' => 'amf#amf_write', :via => :post
   match 'api/0.6/swf/trackpoints' => 'swf#trackpoints', :via => :get
 
+  # Map notes API
+  scope "api/0.6" do
+    resources :notes, :except => [ :new, :edit, :update ], :constraints => { :id => /\d+/ }, :defaults => { :format => "xml" } do
+      collection do
+        get 'search'
+        get 'feed', :defaults => { :format => "rss" }
+      end
+
+      member do
+        post 'comment'
+        post 'close'
+      end
+    end
+
+    match 'notes/addPOIexec' => 'notes#create', :via => :post
+    match 'notes/closePOIexec' => 'notes#close', :via => :post
+    match 'notes/editPOIexec' => 'notes#comment', :via => :post
+    match 'notes/getGPX' => 'notes#index', :via => :get, :format => "gpx"
+    match 'notes/getRSSfeed' => 'notes#feed', :via => :get, :format => "rss"
+  end
+
   # Data browsing
   match '/browse/start' => 'browse#start', :via => :get
   match '/browse/way/:id' => 'browse#way', :via => :get, :id => /\d+/
@@ -87,8 +107,10 @@ OpenStreetMap::Application.routes.draw do
   match '/browse/relation/:id' => 'browse#relation', :via => :get, :id => /\d+/
   match '/browse/relation/:id/history' => 'browse#relation_history', :via => :get, :id => /\d+/
   match '/browse/changeset/:id' => 'browse#changeset', :via => :get, :as => :changeset, :id => /\d+/
+  match '/browse/note/:id' => 'browse#note', :via => :get, :id => /\d+/
   match '/user/:display_name/edits' => 'changeset#list', :via => :get
   match '/user/:display_name/edits/feed' => 'changeset#feed', :via => :get, :format => :atom
+  match '/user/:display_name/notes' => 'notes#mine', :via => :get
   match '/browse/friends' => 'changeset#list', :via => :get, :friends => true, :as => "friend_changesets"
   match '/browse/nearby' => 'changeset#list', :via => :get, :nearby => true, :as => "nearby_changesets"
   match '/browse/changesets' => 'changeset#list', :via => :get
diff --git a/db/migrate/053_add_map_bug_tables.rb b/db/migrate/053_add_map_bug_tables.rb
new file mode 100644 (file)
index 0000000..8d444a4
--- /dev/null
@@ -0,0 +1,33 @@
+require 'lib/migrate'
+
+class AddMapBugTables < ActiveRecord::Migration
+  def self.up
+    create_enumeration :map_bug_status_enum, ["open", "closed", "hidden"]
+
+    create_table :map_bugs do |t|
+      t.column :id, :bigint, :null => false 
+      t.integer :latitude, :null => false 
+      t.integer :longitude, :null => false 
+      t.column :tile, :bigint, :null => false
+      t.datetime :last_changed, :null => false
+      t.datetime :date_created, :null => false 
+      t.string :nearby_place 
+      t.string :text
+      t.column :status, :map_bug_status_enum, :null => false
+    end
+
+    add_index :map_bugs, [:tile, :status], :name => "map_bugs_tile_idx"
+    add_index :map_bugs, [:last_changed], :name => "map_bugs_changed_idx"
+    add_index :map_bugs, [:date_created], :name => "map_bugs_created_idx"
+  end
+
+  def self.down
+    remove_index :map_bugs, :name => "map_bugs_tile_idx"
+    remove_index :map_bugs, :name => "map_bugs_changed_idx"
+    remove_index :map_bugs, :name => "map_bugs_created_idx"
+
+    drop_table :map_bugs
+
+    drop_enumeration :map_bug_status_enum
+  end
+end
diff --git a/db/migrate/054_refactor_map_bug_tables.rb b/db/migrate/054_refactor_map_bug_tables.rb
new file mode 100644 (file)
index 0000000..6d259d2
--- /dev/null
@@ -0,0 +1,34 @@
+require 'lib/migrate'
+
+class RefactorMapBugTables < ActiveRecord::Migration
+  def self.up
+    create_table :map_bug_comment do |t|
+      t.column :id, :bigint, :null => false
+      t.column :bug_id, :bigint, :null => false
+      t.boolean :visible, :null => false 
+      t.datetime :date_created, :null => false
+      t.string :commenter_name
+      t.string :commenter_ip
+      t.column :commenter_id, :bigint
+      t.string :comment
+    end
+
+    remove_column :map_bugs, :text 
+
+    add_index :map_bug_comment, [:bug_id], :name => "map_bug_comment_id_idx"
+
+    add_foreign_key :map_bug_comment, [:bug_id], :map_bugs, [:id]
+    add_foreign_key :map_bug_comment, [:commenter_id], :users, [:id]
+  end
+
+  def self.down
+    remove_foreign_key :map_bug_comment, [:commenter_id]
+    remove_foreign_key :map_bug_comment, [:bug_id]
+
+    remove_index :map_bugs, :name => "map_bug_comment_id_idx"
+
+    add_column :map_bugs, :text, :string
+
+    drop_table :map_bug_comment
+  end
+end
diff --git a/db/migrate/055_change_map_bug_comment_type.rb b/db/migrate/055_change_map_bug_comment_type.rb
new file mode 100644 (file)
index 0000000..2a64bf2
--- /dev/null
@@ -0,0 +1,11 @@
+require 'lib/migrate'
+
+class ChangeMapBugCommentType < ActiveRecord::Migration
+  def self.up
+    change_column :map_bug_comment, :comment, :text
+  end
+
+  def self.down
+    change_column :map_bug_comment, :comment, :string
+  end
+end
diff --git a/db/migrate/056_add_date_closed.rb b/db/migrate/056_add_date_closed.rb
new file mode 100644 (file)
index 0000000..c5aa2c2
--- /dev/null
@@ -0,0 +1,11 @@
+require 'lib/migrate'
+
+class AddDateClosed < ActiveRecord::Migration
+  def self.up
+    add_column :map_bugs, :date_closed, :timestamp
+  end
+
+  def self.down
+    remove_column :map_bugs, :date_closed 
+  end
+end
diff --git a/db/migrate/057_add_map_bug_comment_event.rb b/db/migrate/057_add_map_bug_comment_event.rb
new file mode 100644 (file)
index 0000000..c13c1f9
--- /dev/null
@@ -0,0 +1,15 @@
+require 'lib/migrate'
+
+class AddMapBugCommentEvent < ActiveRecord::Migration
+  def self.up
+    create_enumeration :map_bug_event_enum, ["opened", "closed", "reopened", "commented", "hidden"]
+
+    add_column :map_bug_comment, :event, :map_bug_event_enum
+  end
+
+  def self.down
+    remove_column :map_bug_comment, :event
+
+    drop_enumeration :map_bug_event_enum
+  end
+end
diff --git a/db/migrate/20110508145337_cleanup_bug_tables.rb b/db/migrate/20110508145337_cleanup_bug_tables.rb
new file mode 100644 (file)
index 0000000..e7dfcb7
--- /dev/null
@@ -0,0 +1,25 @@
+class CleanupBugTables < ActiveRecord::Migration
+  def self.up
+    rename_column :map_bugs, :date_created, :created_at
+    rename_column :map_bugs, :last_changed, :updated_at
+    rename_column :map_bugs, :date_closed, :closed_at
+
+    rename_column :map_bug_comment, :date_created, :created_at
+    rename_column :map_bug_comment, :commenter_name, :author_name
+    rename_column :map_bug_comment, :commenter_ip, :author_ip
+    rename_column :map_bug_comment, :commenter_id, :author_id
+    rename_column :map_bug_comment, :comment, :body
+  end
+
+  def self.down
+    rename_column :map_bug_comment, :body, :comment
+    rename_column :map_bug_comment, :author_id, :commenter_id
+    rename_column :map_bug_comment, :author_ip, :commenter_ip
+    rename_column :map_bug_comment, :author_name, :commenter_name
+    rename_column :map_bug_comment, :created_at, :date_created
+
+    rename_column :map_bugs, :closed_at, :date_closed
+    rename_column :map_bugs, :updated_at, :last_changed
+    rename_column :map_bugs, :created_at, :date_created
+  end
+end
diff --git a/db/migrate/20110521142405_rename_bugs_to_notes.rb b/db/migrate/20110521142405_rename_bugs_to_notes.rb
new file mode 100644 (file)
index 0000000..240d447
--- /dev/null
@@ -0,0 +1,55 @@
+require 'lib/migrate'
+
+class RenameBugsToNotes < ActiveRecord::Migration
+  def self.up
+    rename_enumeration "map_bug_status_enum", "note_status_enum"
+    rename_enumeration "map_bug_event_enum", "note_event_enum"
+
+    rename_table :map_bugs, :notes
+    rename_sequence :notes, "map_bugs_id_seq", "notes_id_seq"
+    rename_index :notes, "map_bugs_pkey", "notes_pkey"
+    rename_index :notes, "map_bugs_changed_idx", "notes_updated_at_idx"
+    rename_index :notes, "map_bugs_created_idx", "notes_created_at_idx"
+    rename_index :notes, "map_bugs_tile_idx", "notes_tile_status_idx"
+
+    remove_foreign_key :map_bug_comment, [:bug_id], :map_bugs, [:id]
+    rename_column :map_bug_comment, :author_id, :commenter_id
+    remove_foreign_key :map_bug_comment, [:commenter_id], :users, [:id]
+    rename_column :map_bug_comment, :commenter_id, :author_id
+
+    rename_table :map_bug_comment, :note_comments
+    rename_column :note_comments, :bug_id, :note_id
+    rename_sequence :note_comments, "map_bug_comment_id_seq", "note_comments_id_seq"
+    rename_index :note_comments, "map_bug_comment_pkey", "note_comments_pkey"
+    rename_index :note_comments, "map_bug_comment_id_idx", "note_comments_note_id_idx"
+
+    add_foreign_key :note_comments, [:note_id], :notes, [:id]
+    add_foreign_key :note_comments, [:author_id], :users, [:id]
+  end
+
+  def self.down
+    remove_foreign_key :note_comments, [:author_id], :users, [:id]
+    remove_foreign_key :note_comments, [:note_id], :notes, [:id]
+
+    rename_index :note_comments, "note_comments_note_id_idx", "map_bug_comment_id_idx"
+    rename_index :notes, "note_comments_pkey", "map_bug_comment_pkey"
+    rename_column :note_comments, :note_id, :bug_id
+    rename_sequence :note_comments, "note_comments_id_seq", "map_bug_comment_id_seq"
+    rename_table :note_comments, :map_bug_comment
+
+    rename_column :map_bug_comment, :author_id, :commenter_id
+    add_foreign_key :map_bug_comment, [:commenter_id], :users, [:id]
+    rename_column :map_bug_comment, :commenter_id, :author_id
+    add_foreign_key :map_bug_comment, [:bug_id], :notes, [:id]
+
+    rename_index :notes, "notes_tile_status_idx", "map_bugs_tile_idx"
+    rename_index :notes, "notes_created_at_idx", "map_bugs_created_idx"
+    rename_index :notes, "notes_updated_at_idx", "map_bugs_changed_idx"
+    rename_index :notes, "notes_pkey", "map_bugs_pkey"
+    rename_sequence :notes, "notes_id_seq", "map_bugs_id_seq"
+    rename_table :notes, :map_bugs
+
+    rename_enumeration "note_event_enum", "map_bug_event_enum"
+    rename_enumeration "note_status_enum", "map_bug_status_enum"
+  end
+end
index 5f71a9fb4962a416bedb4d9125409a328f783efe..fb5069bf94791fe500f42752410a5812f4928465 100644 (file)
@@ -61,6 +61,30 @@ CREATE TYPE gpx_visibility_enum AS ENUM (
 );
 
 
+--
+-- Name: note_event_enum; Type: TYPE; Schema: public; Owner: -
+--
+
+CREATE TYPE note_event_enum AS ENUM (
+    'opened',
+    'closed',
+    'reopened',
+    'commented',
+    'hidden'
+);
+
+
+--
+-- Name: note_status_enum; Type: TYPE; Schema: public; Owner: -
+--
+
+CREATE TYPE note_status_enum AS ENUM (
+    'open',
+    'closed',
+    'hidden'
+);
+
+
 --
 -- Name: nwr_enum; Type: TYPE; Schema: public; Owner: -
 --
@@ -101,7 +125,7 @@ CREATE TYPE user_status_enum AS ENUM (
 
 CREATE FUNCTION maptile_for_point(bigint, bigint, integer) RETURNS integer
     LANGUAGE c STRICT
-    AS '/srv/www/master.osm.compton.nu/db/functions/libpgosm.so', 'maptile_for_point';
+    AS '/srv/www/openstreetbugs.osm.compton.nu/db/functions/libpgosm.so', 'maptile_for_point';
 
 
 --
@@ -110,7 +134,7 @@ CREATE FUNCTION maptile_for_point(bigint, bigint, integer) RETURNS integer
 
 CREATE FUNCTION tile_for_point(integer, integer) RETURNS bigint
     LANGUAGE c STRICT
-    AS '/srv/www/master.osm.compton.nu/db/functions/libpgosm.so', 'tile_for_point';
+    AS '/srv/www/openstreetbugs.osm.compton.nu/db/functions/libpgosm.so', 'tile_for_point';
 
 
 --
@@ -119,7 +143,7 @@ CREATE FUNCTION tile_for_point(integer, integer) RETURNS bigint
 
 CREATE FUNCTION xid_to_int4(xid) RETURNS integer
     LANGUAGE c IMMUTABLE STRICT
-    AS '/srv/www/master.osm.compton.nu/db/functions/libpgosm.so', 'xid_to_int4';
+    AS '/srv/www/openstreetbugs.osm.compton.nu/db/functions/libpgosm.so', 'xid_to_int4';
 
 
 SET default_tablespace = '';
@@ -700,6 +724,78 @@ CREATE TABLE nodes (
 );
 
 
+--
+-- Name: note_comments; Type: TABLE; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE TABLE note_comments (
+    id integer NOT NULL,
+    note_id bigint NOT NULL,
+    visible boolean NOT NULL,
+    created_at timestamp without time zone NOT NULL,
+    author_name character varying(255),
+    author_ip character varying(255),
+    author_id bigint,
+    body text,
+    event note_event_enum
+);
+
+
+--
+-- Name: note_comments_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE note_comments_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+--
+-- Name: note_comments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE note_comments_id_seq OWNED BY note_comments.id;
+
+
+--
+-- Name: notes; Type: TABLE; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE TABLE notes (
+    id integer NOT NULL,
+    latitude integer NOT NULL,
+    longitude integer NOT NULL,
+    tile bigint NOT NULL,
+    updated_at timestamp without time zone NOT NULL,
+    created_at timestamp without time zone NOT NULL,
+    nearby_place character varying(255),
+    status note_status_enum NOT NULL,
+    closed_at timestamp without time zone
+);
+
+
+--
+-- Name: notes_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE notes_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+--
+-- Name: notes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE notes_id_seq OWNED BY notes.id;
+
+
 --
 -- Name: oauth_nonces; Type: TABLE; Schema: public; Owner: -; Tablespace: 
 --
@@ -1000,11 +1096,11 @@ CREATE TABLE users (
     status user_status_enum DEFAULT 'pending'::user_status_enum NOT NULL,
     terms_agreed timestamp without time zone,
     consider_pd boolean DEFAULT false NOT NULL,
+    openid_url character varying(255),
     preferred_editor character varying(255),
     terms_seen boolean DEFAULT false NOT NULL,
-    openid_url character varying(255),
-    description_format format_enum DEFAULT 'html'::format_enum NOT NULL,
     image_fingerprint character varying(255),
+    description_format format_enum DEFAULT 'html'::format_enum NOT NULL,
     changesets_count integer DEFAULT 0 NOT NULL,
     traces_count integer DEFAULT 0 NOT NULL
 );
@@ -1158,6 +1254,20 @@ ALTER TABLE ONLY gpx_files ALTER COLUMN id SET DEFAULT nextval('gpx_files_id_seq
 ALTER TABLE ONLY messages ALTER COLUMN id SET DEFAULT nextval('messages_id_seq'::regclass);
 
 
+--
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY note_comments ALTER COLUMN id SET DEFAULT nextval('note_comments_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY notes ALTER COLUMN id SET DEFAULT nextval('notes_id_seq'::regclass);
+
+
 --
 -- Name: id; Type: DEFAULT; Schema: public; Owner: -
 --
@@ -1375,6 +1485,22 @@ ALTER TABLE ONLY nodes
     ADD CONSTRAINT nodes_pkey PRIMARY KEY (node_id, version);
 
 
+--
+-- Name: note_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: 
+--
+
+ALTER TABLE ONLY note_comments
+    ADD CONSTRAINT note_comments_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: notes_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: 
+--
+
+ALTER TABLE ONLY notes
+    ADD CONSTRAINT notes_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: oauth_nonces_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: 
 --
@@ -1725,6 +1851,34 @@ CREATE INDEX nodes_tile_idx ON nodes USING btree (tile);
 CREATE INDEX nodes_timestamp_idx ON nodes USING btree ("timestamp");
 
 
+--
+-- Name: note_comments_note_id_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE INDEX note_comments_note_id_idx ON note_comments USING btree (note_id);
+
+
+--
+-- Name: notes_created_at_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE INDEX notes_created_at_idx ON notes USING btree (created_at);
+
+
+--
+-- Name: notes_tile_status_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE INDEX notes_tile_status_idx ON notes USING btree (tile, status);
+
+
+--
+-- Name: notes_updated_at_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE INDEX notes_updated_at_idx ON notes USING btree (updated_at);
+
+
 --
 -- Name: points_gpxid_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
@@ -2059,6 +2213,22 @@ ALTER TABLE ONLY nodes
     ADD CONSTRAINT nodes_redaction_id_fkey FOREIGN KEY (redaction_id) REFERENCES redactions(id);
 
 
+--
+-- Name: note_comments_author_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY note_comments
+    ADD CONSTRAINT note_comments_author_id_fkey FOREIGN KEY (author_id) REFERENCES users(id);
+
+
+--
+-- Name: note_comments_note_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY note_comments
+    ADD CONSTRAINT note_comments_note_id_fkey FOREIGN KEY (note_id) REFERENCES notes(id);
+
+
 --
 -- Name: oauth_tokens_client_application_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
 --
@@ -2243,6 +2413,10 @@ INSERT INTO schema_migrations (version) VALUES ('20101114011429');
 
 INSERT INTO schema_migrations (version) VALUES ('20110322001319');
 
+INSERT INTO schema_migrations (version) VALUES ('20110508145337');
+
+INSERT INTO schema_migrations (version) VALUES ('20110521142405');
+
 INSERT INTO schema_migrations (version) VALUES ('20110925112722');
 
 INSERT INTO schema_migrations (version) VALUES ('20111116184519');
@@ -2337,6 +2511,16 @@ INSERT INTO schema_migrations (version) VALUES ('51');
 
 INSERT INTO schema_migrations (version) VALUES ('52');
 
+INSERT INTO schema_migrations (version) VALUES ('53');
+
+INSERT INTO schema_migrations (version) VALUES ('54');
+
+INSERT INTO schema_migrations (version) VALUES ('55');
+
+INSERT INTO schema_migrations (version) VALUES ('56');
+
+INSERT INTO schema_migrations (version) VALUES ('57');
+
 INSERT INTO schema_migrations (version) VALUES ('6');
 
 INSERT INTO schema_migrations (version) VALUES ('7');
index e560dbefa6c7f3e4c1c7bdf975b0a26917bd953a..38b994d26a6e11233983adcf6cbd730e5198af70 100644 (file)
@@ -37,6 +37,13 @@ public
     from_bbox_array(bbox_array)
   end
 
+  def self.from_lrbt_params(params)
+    if params[:l] and params[:b] and params[:t] and params[:t]
+      bbox_array = [params[:l], params[:b], params[:r], params[:t]]
+    end
+    from_bbox_array(bbox_array)
+  end
+
   def expand!(bbox, margin = 0)
     update!(bbox) unless complete?
     # only try to expand the bbox if there is a value for every coordinate
@@ -71,10 +78,10 @@ public
     self
   end
 
-  def check_size
+  def check_size(max_area = MAX_REQUEST_AREA)
     # check the bbox isn't too large
-    if area > MAX_REQUEST_AREA
-      raise OSM::APIBadBoundingBox.new("The maximum bbox size is " + MAX_REQUEST_AREA.to_s +
+    if area > max_area
+      raise OSM::APIBadBoundingBox.new("The maximum bbox size is " + max_area.to_s +
         ", and your request was too large. Either request a smaller area, or use planet.osm")
     end
     self
index 0d010eb869b6f6116c5024e2fa8bff300acf8d68..0c261dc69c8d3330f5ad4ea17b603e8a848dd9e1 100644 (file)
@@ -45,4 +45,3 @@ private
     180/Math::PI * Math.log(Math.tan(Math::PI/4+a*(Math::PI/180)/2))
   end
 end
-
index 7549add2abfb7e5623a23dbd26f8981c0e4ab6a2..75e047815a073004eb8f5c7fc8467f1840a9f944 100644 (file)
@@ -87,18 +87,23 @@ module ActiveRecord
         @enumerations ||= Hash.new
       end
 
-      def create_enumeration (enumeration_name, values)
+      def create_enumeration(enumeration_name, values)
         enumerations[enumeration_name] = values
-        execute "create type #{enumeration_name} as enum ('#{values.join '\',\''}')"
+        execute "CREATE TYPE #{enumeration_name} AS ENUM ('#{values.join '\',\''}')"
       end
 
-      def drop_enumeration (enumeration_name)
-        execute "drop type #{enumeration_name}"
+      def drop_enumeration(enumeration_name)
+        execute "DROP TYPE #{enumeration_name}"
         enumerations.delete(enumeration_name)
       end
 
+      def rename_enumeration(old_name, new_name)
+        execute "ALTER TYPE #{quote_table_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
+      end
+
       def alter_primary_key(table_name, new_columns)
-        execute "alter table #{table_name} drop constraint #{table_name}_pkey; alter table #{table_name} add primary key (#{new_columns.join(',')})"
+        execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{table_name}_pkey"
+        execute "ALTER TABLE #{table_name} ADD PRIMARY KEY (#{new_columns.join(',')})"
       end
 
       def interval_constant(interval)
@@ -125,6 +130,14 @@ module ActiveRecord
 
         execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} USING #{index_method} (#{quoted_column_names})"
       end
+
+      def rename_index(table_name, old_name, new_name)
+        execute "ALTER INDEX #{quote_table_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
+      end
+
+      def rename_sequence(table_name, old_name, new_name)
+        execute "ALTER SEQUENCE #{quote_table_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
+      end
     end
   end
 end
diff --git a/test/fixtures/note_comments.yml b/test/fixtures/note_comments.yml
new file mode 100644 (file)
index 0000000..e078b99
--- /dev/null
@@ -0,0 +1,117 @@
+t1:
+  id: 1
+  note_id: 1
+  visible: true
+  created_at: 2007-01-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'This is the initial description of the note 1'
+
+t2:
+  id: 2
+  note_id: 2
+  visible: true
+  created_at: 2007-01-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'This is the initial description of the note 2'
+
+t3:
+  id: 3
+  note_id: 2
+  visible: true
+  created_at: 2007-02-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'This is an additional comment for note 2'
+
+t4:
+  id: 4
+  note_id: 3
+  visible: true
+  created_at: 2007-01-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'This is the initial comment for note 3'
+
+t5:
+  id: 5
+  note_id: 4
+  visible: true
+  created_at: 2007-01-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'Spam for note 4'
+
+t6:
+  id: 6
+  note_id: 5
+  visible: true
+  created_at: 2007-01-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'Valid comment for note 5'
+
+t7:
+  id: 7
+  note_id: 5
+  visible: false
+  created_at: 2007-02-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'Spam for note 5'
+
+t8:
+  id: 8
+  note_id: 5
+  visible: true
+  created_at: 2007-02-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'Another valid comment for note 5'
+
+t9:
+  id: 9
+  note_id: 6
+  visible: true
+  created_at: 2007-01-01 00:00:00
+  event: opened
+  author_id: 1
+  body: 'This is a note with from a logged-in user'
+
+t10:
+  id: 10
+  note_id: 6
+  visible: true
+  created_at: 2007-02-01 00:00:00
+  event: commented
+  author_id: 4
+  body: 'A comment from another logged-in user'
+
+t11:
+  id: 11
+  note_id: 7
+  visible: true
+  event: opened
+  created_at: 2007-01-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'Initial note description'
+
+t12:
+  id: 12
+  note_id: 7
+  visible: true
+  event: commented
+  created_at: 2007-02-01 00:00:00
+  author_name: 'testname'
+  author_ip: '192.168.1.1'
+  body: 'A comment description'
+
+t13:
+  id: 13
+  note_id: 7
+  visible: true
+  event: closed
+  created_at: 2007-03-01 00:00:00
+  author_id: 4
diff --git a/test/fixtures/notes.yml b/test/fixtures/notes.yml
new file mode 100644 (file)
index 0000000..ffecba8
--- /dev/null
@@ -0,0 +1,67 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+<% SCALE = 10000000 unless defined?(SCALE) %>
+
+open_note:
+  id: 1
+  latitude: <%= 1*SCALE %>
+  longitude: <%= 1*SCALE %>
+  status: open
+  tile: <%= QuadTile.tile_for_point(1,1) %>
+  created_at: 2007-01-01 00:00:00
+  updated_at: 2007-01-01 00:00:00
+
+open_note_with_comment:
+  id: 2
+  latitude: <%= 1.1*SCALE %>
+  longitude: <%= 1.1*SCALE %>
+  status: open
+  tile: <%= QuadTile.tile_for_point(1.1,1.1) %>
+  created_at: 2007-01-01 00:00:00
+  updated_at: 2007-02-01 00:00:00
+
+closed_note_with_comment:
+  id: 3
+  latitude: <%= 1.2*SCALE %>
+  longitude: <%= 1.2*SCALE %>
+  status: closed
+  tile: <%= QuadTile.tile_for_point(1.2,1.2) %>
+  created_at: 2007-01-01 00:00:00
+  updated_at: 2007-03-01 00:00:00
+  closed_at:  2007-03-01 00:00:00
+
+hidden_note_with_comment:
+  id: 4
+  latitude: <%= 1.3*SCALE %>
+  longitude: <%= 1.3*SCALE %>
+  status: hidden
+  tile: <%= QuadTile.tile_for_point(1.3,1.3) %>
+  created_at: 2007-01-01 00:00:00
+  updated_at: 2007-03-01 00:00:00
+
+note_with_hidden_comment:
+  id: 5
+  latitude: <%= 1.4*SCALE %>
+  longitude: <%= 1.4*SCALE %>
+  status: open
+  tile: <%= QuadTile.tile_for_point(1.4,1.4) %>
+  created_at: 2007-01-01 00:00:00
+  updated_at: 2007-03-01 00:00:00
+
+note_with_comments_by_users:
+  id: 6
+  latitude: <%= 1.5*SCALE %>
+  longitude: <%= 1.5*SCALE %>
+  status: open
+  tile: <%= QuadTile.tile_for_point(1.5,1.5) %>
+  created_at: 2007-01-01 00:00:00
+  updated_at: 2007-03-01 00:00:00
+
+note_closed_by_user:
+  id: 7
+  latitude: <%= 1.6*SCALE %>
+  longitude: <%= 1.6*SCALE %>
+  status: closed
+  tile: <%= QuadTile.tile_for_point(1.6,1.6) %>
+  created_at: 2007-01-01 00:00:00
+  updated_at: 2007-03-01 00:00:00
+  closed_at:  2007-03-01 00:00:00
index 4cc19b2d66aa58ebaa6510cd59d91b0acb767591..63c3d0b04555857926eb46562190d2fdfc3a2e6c 100644 (file)
@@ -39,6 +39,10 @@ class BrowseControllerTest < ActionController::TestCase
       { :path => "/browse/changeset/1", :method => :get },
       { :controller => "browse", :action => "changeset", :id => "1" }
     )
+    assert_routing(
+      { :path => "/browse/note/1", :method => :get },
+      { :controller => "browse", :action => "note", :id => "1" }
+    )
   end
 
   def test_start
diff --git a/test/functional/notes_controller_test.rb b/test/functional/notes_controller_test.rb
new file mode 100644 (file)
index 0000000..1c50ee8
--- /dev/null
@@ -0,0 +1,442 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class NotesControllerTest < ActionController::TestCase
+  fixtures :users, :notes, :note_comments
+
+  ##
+  # test all routes which lead to this controller
+  def test_routes
+    assert_routing(
+      { :path => "/api/0.6/notes", :method => :post },
+      { :controller => "notes", :action => "create", :format => "xml" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/1", :method => :get },
+      { :controller => "notes", :action => "show", :id => "1", :format => "xml" }
+    )
+    assert_recognizes(
+      { :controller => "notes", :action => "show", :id => "1", :format => "xml" },
+      { :path => "/api/0.6/notes/1.xml", :method => :get }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/1.rss", :method => :get },
+      { :controller => "notes", :action => "show", :id => "1", :format => "rss" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/1.json", :method => :get },
+      { :controller => "notes", :action => "show", :id => "1", :format => "json" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/1.gpx", :method => :get },
+      { :controller => "notes", :action => "show", :id => "1", :format => "gpx" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/1/comment", :method => :post },
+      { :controller => "notes", :action => "comment", :id => "1", :format => "xml" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/1/close", :method => :post },
+      { :controller => "notes", :action => "close", :id => "1", :format => "xml" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/1", :method => :delete },
+      { :controller => "notes", :action => "destroy", :id => "1", :format => "xml" }
+    )
+
+    assert_routing(
+      { :path => "/api/0.6/notes", :method => :get },
+      { :controller => "notes", :action => "index", :format => "xml" }
+    )
+    assert_recognizes(
+      { :controller => "notes", :action => "index", :format => "xml" },
+      { :path => "/api/0.6/notes.xml", :method => :get }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes.rss", :method => :get },
+      { :controller => "notes", :action => "index", :format => "rss" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes.json", :method => :get },
+      { :controller => "notes", :action => "index", :format => "json" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes.gpx", :method => :get },
+      { :controller => "notes", :action => "index", :format => "gpx" }
+    )
+
+    assert_routing(
+      { :path => "/api/0.6/notes/search", :method => :get },
+      { :controller => "notes", :action => "search", :format => "xml" }
+    )
+    assert_recognizes(
+      { :controller => "notes", :action => "search", :format => "xml" },
+      { :path => "/api/0.6/notes/search.xml", :method => :get }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/search.rss", :method => :get },
+      { :controller => "notes", :action => "search", :format => "rss" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/search.json", :method => :get },
+      { :controller => "notes", :action => "search", :format => "json" }
+    )
+    assert_routing(
+      { :path => "/api/0.6/notes/search.gpx", :method => :get },
+      { :controller => "notes", :action => "search", :format => "gpx" }
+    )
+
+    assert_routing(
+      { :path => "/api/0.6/notes/feed", :method => :get },
+      { :controller => "notes", :action => "feed", :format => "rss" }
+    )
+
+    assert_recognizes(
+      { :controller => "notes", :action => "create" },
+      { :path => "/api/0.6/notes/addPOIexec", :method => :post }
+    )
+    assert_recognizes(
+      { :controller => "notes", :action => "close" },
+      { :path => "/api/0.6/notes/closePOIexec", :method => :post }
+    )
+    assert_recognizes(
+      { :controller => "notes", :action => "comment" },
+      { :path => "/api/0.6/notes/editPOIexec", :method => :post }
+    )
+    assert_recognizes(
+      { :controller => "notes", :action => "index", :format => "gpx" },
+      { :path => "/api/0.6/notes/getGPX", :method => :get }
+    )
+    assert_recognizes(
+      { :controller => "notes", :action => "feed", :format => "rss" },
+      { :path => "/api/0.6/notes/getRSSfeed", :method => :get }
+    )
+
+    assert_routing(
+      { :path => "/user/username/notes", :method => :get },
+      { :controller => "notes", :action => "mine", :display_name => "username" }
+    )
+  end
+
+  def test_note_create_success
+    assert_difference('Note.count') do
+      assert_difference('NoteComment.count') do
+        post :create, {:lat => -1.0, :lon => -1.0, :name => "new_tester", :text => "This is a comment"}
+      end
+    end
+    assert_response :success
+    id = @response.body.sub(/ok/,"").to_i
+
+    get :show, {:id => id, :format => 'json'}
+    assert_response :success
+    js = ActiveSupport::JSON.decode(@response.body)
+    assert_not_nil js
+    assert_equal "Feature", js["type"]
+    assert_equal "Point", js["geometry"]["type"]
+    assert_equal [-1.0, -1.0], js["geometry"]["coordinates"]
+    assert_equal id, js["properties"]["id"]
+    assert_equal "open", js["properties"]["status"]
+    assert_equal 1, js["properties"]["comments"].count
+    assert_equal "opened", js["properties"]["comments"].last["action"]
+    assert_equal "This is a comment", js["properties"]["comments"].last["text"]
+    assert_equal "new_tester (a)", js["properties"]["comments"].last["user"]
+  end
+
+  def test_note_create_fail
+    assert_no_difference('Note.count') do
+      assert_no_difference('NoteComment.count') do
+        post :create, {:lon => -1.0, :name => "new_tester", :text => "This is a comment"}
+      end
+    end
+    assert_response :bad_request
+
+    assert_no_difference('Note.count') do
+      assert_no_difference('NoteComment.count') do
+        post :create, {:lat => -1.0, :name => "new_tester", :text => "This is a comment"}
+      end
+    end
+    assert_response :bad_request
+
+    assert_no_difference('Note.count') do
+      assert_no_difference('NoteComment.count') do
+        post :create, {:lat => -1.0, :lon => -1.0, :name => "new_tester"}
+      end
+    end
+    assert_response :bad_request
+
+    assert_no_difference('Note.count') do
+      assert_no_difference('NoteComment.count') do
+        post :create, {:lat => -100.0, :lon => -1.0, :name => "new_tester", :text => "This is a comment"}
+      end
+    end
+    assert_response :bad_request
+
+    assert_no_difference('Note.count') do
+      assert_no_difference('NoteComment.count') do
+        post :create, {:lat => -1.0, :lon => -200.0, :name => "new_tester", :text => "This is a comment"}
+      end
+    end
+    assert_response :bad_request
+  end
+
+  def test_note_comment_create_success
+    assert_difference('NoteComment.count') do
+      post :comment, {:id => notes(:open_note_with_comment).id, :name => "new_tester2", :text => "This is an additional comment"}
+    end
+    assert_response :success
+
+    get :show, {:id => notes(:open_note_with_comment).id, :format => 'json'}
+    assert_response :success
+    js = ActiveSupport::JSON.decode(@response.body)
+    assert_not_nil js
+    assert_equal "Feature", js["type"]
+    assert_equal notes(:open_note_with_comment).id, js["properties"]["id"]
+    assert_equal "open", js["properties"]["status"]
+    assert_equal 3, js["properties"]["comments"].count
+    assert_equal "commented", js["properties"]["comments"].last["action"]
+    assert_equal "This is an additional comment", js["properties"]["comments"].last["text"]
+    assert_equal "new_tester2 (a)", js["properties"]["comments"].last["user"]
+  end
+
+  def test_note_comment_create_fail
+    assert_no_difference('NoteComment.count') do
+      post :comment, {:name => "new_tester2", :text => "This is an additional comment"}
+    end
+    assert_response :bad_request
+
+    assert_no_difference('NoteComment.count') do
+      post :comment, {:id => notes(:open_note_with_comment).id, :name => "new_tester2"}
+    end
+    assert_response :bad_request
+
+    assert_no_difference('NoteComment.count') do
+      post :comment, {:id => 12345, :name => "new_tester2", :text => "This is an additional comment"}
+    end
+    assert_response :not_found
+
+    assert_no_difference('NoteComment.count') do
+      post :comment, {:id => notes(:hidden_note_with_comment).id, :name => "new_tester2", :text => "This is an additional comment"}
+    end
+    assert_response :gone
+  end
+
+  def test_note_close_success
+    post :close, {:id => notes(:open_note_with_comment).id}
+    assert_response :success
+
+    get :show, {:id => notes(:open_note_with_comment).id, :format => 'json'}
+    assert_response :success
+    js = ActiveSupport::JSON.decode(@response.body)
+    assert_not_nil js
+    assert_equal "Feature", js["type"]
+    assert_equal notes(:open_note_with_comment).id, js["properties"]["id"]
+    assert_equal "closed", js["properties"]["status"]
+    assert_equal 3, js["properties"]["comments"].count
+    assert_equal "closed", js["properties"]["comments"].last["action"]
+    assert_equal nil, js["properties"]["comments"].last["text"]
+    assert_equal "NoName (a)", js["properties"]["comments"].last["user"]
+  end
+
+  def test_note_close_fail
+    post :close
+    assert_response :bad_request
+
+    post :close, {:id => 12345}
+    assert_response :not_found
+
+    post :close, {:id => notes(:hidden_note_with_comment).id}
+    assert_response :gone
+  end
+
+  def test_note_read_success
+    get :show, {:id => notes(:open_note).id, :format => "xml"}
+    assert_response :success
+    assert_equal "application/xml", @response.content_type
+
+    get :show, {:id => notes(:open_note).id, :format => "rss"}
+    assert_response :success
+    assert_equal "application/rss+xml", @response.content_type
+
+    get :show, {:id => notes(:open_note).id, :format => "json"}
+    assert_response :success
+    assert_equal "application/json", @response.content_type
+
+    get :show, {:id => notes(:open_note).id, :format => "gpx"}
+    assert_response :success
+    assert_equal "application/gpx+xml", @response.content_type
+  end
+
+  def test_note_read_hidden_comment
+    get :show, {:id => notes(:note_with_hidden_comment).id, :format => 'json'}
+    assert_response :success
+    js = ActiveSupport::JSON.decode(@response.body)
+    assert_not_nil js
+    assert_equal notes(:note_with_hidden_comment).id, js["properties"]["id"]
+    assert_equal 2, js["properties"]["comments"].count
+    assert_equal "Valid comment for note 5", js["properties"]["comments"][0]["text"]
+    assert_equal "Another valid comment for note 5", js["properties"]["comments"][1]["text"]
+  end
+
+  def test_note_read_fail
+    post :show
+    assert_response :bad_request
+
+    get :show, {:id => 12345}
+    assert_response :not_found
+
+    get :show, {:id => notes(:hidden_note_with_comment).id}
+    assert_response :gone
+  end
+
+  def test_note_delete_success
+    delete :destroy, {:id => notes(:open_note_with_comment).id}
+    assert_response :success
+
+    get :show, {:id => notes(:open_note_with_comment).id, :format => 'json'}
+    assert_response :gone
+  end
+
+  def test_note_delete_fail
+    delete :destroy
+    assert_response :bad_request
+
+    delete :destroy, {:id => 12345}
+    assert_response :not_found
+
+    delete :destroy, {:id => notes(:hidden_note_with_comment).id}
+    assert_response :gone
+  end
+
+  def test_get_notes_success
+#    get :index, {:bbox => '1,1,1.2,1.2'}
+#    assert_response :success
+#    assert_equal "text/javascript", @response.content_type
+
+    get :index, {:bbox => '1,1,1.2,1.2', :format => 'rss'}
+    assert_response :success
+    assert_equal "application/rss+xml", @response.content_type
+
+    get :index, {:bbox => '1,1,1.2,1.2', :format => 'json'}
+    assert_response :success
+    assert_equal "application/json", @response.content_type
+
+    get :index, {:bbox => '1,1,1.2,1.2', :format => 'xml'}
+    assert_response :success
+    assert_equal "application/xml", @response.content_type
+
+    get :index, {:bbox => '1,1,1.2,1.2', :format => 'gpx'}
+    assert_response :success
+    assert_equal "application/gpx+xml", @response.content_type
+  end
+
+  def test_get_notes_large_area
+#    get :index, {:bbox => '-2.5,-2.5,2.5,2.5'}
+#    assert_response :success
+
+#    get :index, {:l => '-2.5', :b => '-2.5', :r => '2.5', :t => '2.5'}
+#    assert_response :success
+
+    get :index, {:bbox => '-10,-10,12,12'}
+    assert_response :bad_request
+
+    get :index, {:l => '-10', :b => '-10', :r => '12', :t => '12'}
+    assert_response :bad_request
+  end
+
+  def test_get_notes_closed
+    get :index, {:bbox => '1,1,1.7,1.7', :closed => '7', :format => 'json'}
+    assert_response :success
+    assert_equal "application/json", @response.content_type
+    js = ActiveSupport::JSON.decode(@response.body)
+    assert_not_nil js
+    assert_equal "FeatureCollection", js["type"]
+    assert_equal 4, js["features"].count
+
+    get :index, {:bbox => '1,1,1.7,1.7', :closed => '0', :format => 'json'}
+    assert_response :success
+    assert_equal "application/json", @response.content_type
+    js = ActiveSupport::JSON.decode(@response.body)
+    assert_not_nil js
+    assert_equal "FeatureCollection", js["type"]
+    assert_equal 4, js["features"].count
+
+    get :index, {:bbox => '1,1,1.7,1.7', :closed => '-1', :format => 'json'}
+    assert_response :success
+    assert_equal "application/json", @response.content_type
+    js = ActiveSupport::JSON.decode(@response.body)
+    assert_not_nil js
+    assert_equal "FeatureCollection", js["type"]
+    assert_equal 6, js["features"].count
+  end
+
+  def test_get_notes_bad_params
+    get :index, {:bbox => '-2.5,-2.5,2.5'}
+    assert_response :bad_request
+
+    get :index, {:bbox => '-2.5,-2.5,2.5,2.5,2.5'}
+    assert_response :bad_request
+
+    get :index, {:b => '-2.5', :r => '2.5', :t => '2.5'}
+    assert_response :bad_request
+
+    get :index, {:l => '-2.5', :r => '2.5', :t => '2.5'}
+    assert_response :bad_request
+
+    get :index, {:l => '-2.5', :b => '-2.5', :t => '2.5'}
+    assert_response :bad_request
+
+    get :index, {:l => '-2.5', :b => '-2.5', :r => '2.5'}
+    assert_response :bad_request
+  end
+
+  def test_search_success
+    get :search, {:q => 'note 1', :format => 'xml'}
+    assert_response :success
+    assert_equal "application/xml", @response.content_type
+
+    get :search, {:q => 'note 1', :format => 'json'}
+    assert_response :success
+    assert_equal "application/json", @response.content_type
+
+    get :search, {:q => 'note 1', :format => 'rss'}
+    assert_response :success
+    assert_equal "application/rss+xml", @response.content_type
+
+    get :search, {:q => 'note 1', :format => 'gpx'}
+    assert_response :success
+    assert_equal "application/gpx+xml", @response.content_type
+  end
+
+  def test_search_bad_params
+    get :search
+    assert_response :bad_request
+  end
+
+  def test_rss_success
+    get :feed, {:format => 'rss'}
+    assert_response :success
+    assert_equal "application/rss+xml", @response.content_type
+
+    get :feed, {:bbox=>'1,1,1.2,1.2', :format => 'rss'}
+    assert_response :success   
+    assert_equal "application/rss+xml", @response.content_type
+  end
+
+  def test_rss_fail
+    get :feed, {:bbox=>'1,1,1.2'}
+    assert_response :bad_request
+
+    get :feed, {:bbox=>'1,1,1.2,1.2,1.2'}
+    assert_response :bad_request
+  end
+
+  def test_user_notes_success
+    get :mine, {:display_name=>'test'}
+    assert_response :success
+
+    get :mine, {:display_name=>'pulibc_test2'}
+    assert_response :success
+
+    get :mine, {:display_name=>'non-existent'}
+    assert_response :not_found 
+  end
+end
index ae17272adf401156b7c03fab6cfe0b166cd2126b..b3705cb900fe1aa1f8843e9ee29eb36c74e62772 100644 (file)
@@ -255,7 +255,30 @@ element=element.parentNode;}while(--depth>0&&element);return button;},buttonClic
 if(this.cancelRegEx.test(evt.type)){delete this.startEvt;}
 OpenLayers.Event.stop(evt);propagate=false;}
 if(this.startRegEx.test(evt.type)){this.startEvt=evt;OpenLayers.Event.stop(evt);propagate=false;}}else{delete this.startEvt;}}
-return propagate;}});OpenLayers.Lang["is"]=OpenLayers.Util.applyDefaults({'Permalink':"Varanlegur tengill",'Overlays':"Þekjur",'Base Layer':"Grunnlag",'Scale = 1 : ${scaleDenom}':"Skali = 1 : ${scaleDenom}",'methodDeprecated':"Þetta fall hefur verið úrelt og verður fjarlægt í 3.0. Notaðu ${newMethod} í staðin."});OpenLayers.Geometry=OpenLayers.Class({id:null,parent:null,bounds:null,initialize:function(){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){this.id=null;this.bounds=null;},clone:function(){return new OpenLayers.Geometry();},setBounds:function(bounds){if(bounds){this.bounds=bounds.clone();}},clearBounds:function(){this.bounds=null;if(this.parent){this.parent.clearBounds();}},extendBounds:function(newBounds){var bounds=this.getBounds();if(!bounds){this.setBounds(newBounds);}else{this.bounds.extend(newBounds);}},getBounds:function(){if(this.bounds==null){this.calculateBounds();}
+return propagate;}});OpenLayers.Lang["is"]=OpenLayers.Util.applyDefaults({'Permalink':"Varanlegur tengill",'Overlays':"Þekjur",'Base Layer':"Grunnlag",'Scale = 1 : ${scaleDenom}':"Skali = 1 : ${scaleDenom}",'methodDeprecated':"Þetta fall hefur verið úrelt og verður fjarlægt í 3.0. Notaðu ${newMethod} í staðin."});OpenLayers.Format=OpenLayers.Class({options:null,externalProjection:null,internalProjection:null,data:null,keepData:false,initialize:function(options){OpenLayers.Util.extend(this,options);this.options=options;},destroy:function(){},read:function(data){throw new Error('Read not implemented.');},write:function(object){throw new Error('Write not implemented.');},CLASS_NAME:"OpenLayers.Format"});OpenLayers.Format.JSON=OpenLayers.Class(OpenLayers.Format,{indent:"    ",space:" ",newline:"\n",level:0,pretty:false,nativeJSON:(function(){return!!(window.JSON&&typeof JSON.parse=="function"&&typeof JSON.stringify=="function");})(),read:function(json,filter){var object;if(this.nativeJSON){object=JSON.parse(json,filter);}else try{if(/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){object=eval('('+json+')');if(typeof filter==='function'){function walk(k,v){if(v&&typeof v==='object'){for(var i in v){if(v.hasOwnProperty(i)){v[i]=walk(i,v[i]);}}}
+return filter(k,v);}
+object=walk('',object);}}}catch(e){}
+if(this.keepData){this.data=object;}
+return object;},write:function(value,pretty){this.pretty=!!pretty;var json=null;var type=typeof value;if(this.serialize[type]){try{json=(!this.pretty&&this.nativeJSON)?JSON.stringify(value):this.serialize[type].apply(this,[value]);}catch(err){OpenLayers.Console.error("Trouble serializing: "+err);}}
+return json;},writeIndent:function(){var pieces=[];if(this.pretty){for(var i=0;i<this.level;++i){pieces.push(this.indent);}}
+return pieces.join('');},writeNewline:function(){return(this.pretty)?this.newline:'';},writeSpace:function(){return(this.pretty)?this.space:'';},serialize:{'object':function(object){if(object==null){return"null";}
+if(object.constructor==Date){return this.serialize.date.apply(this,[object]);}
+if(object.constructor==Array){return this.serialize.array.apply(this,[object]);}
+var pieces=['{'];this.level+=1;var key,keyJSON,valueJSON;var addComma=false;for(key in object){if(object.hasOwnProperty(key)){keyJSON=OpenLayers.Format.JSON.prototype.write.apply(this,[key,this.pretty]);valueJSON=OpenLayers.Format.JSON.prototype.write.apply(this,[object[key],this.pretty]);if(keyJSON!=null&&valueJSON!=null){if(addComma){pieces.push(',');}
+pieces.push(this.writeNewline(),this.writeIndent(),keyJSON,':',this.writeSpace(),valueJSON);addComma=true;}}}
+this.level-=1;pieces.push(this.writeNewline(),this.writeIndent(),'}');return pieces.join('');},'array':function(array){var json;var pieces=['['];this.level+=1;for(var i=0,len=array.length;i<len;++i){json=OpenLayers.Format.JSON.prototype.write.apply(this,[array[i],this.pretty]);if(json!=null){if(i>0){pieces.push(',');}
+pieces.push(this.writeNewline(),this.writeIndent(),json);}}
+this.level-=1;pieces.push(this.writeNewline(),this.writeIndent(),']');return pieces.join('');},'string':function(string){var m={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};if(/["\\\x00-\x1f]/.test(string)){return'"'+string.replace(/([\x00-\x1f\\"])/g,function(a,b){var c=m[b];if(c){return c;}
+c=b.charCodeAt();return'\\u00'+
+Math.floor(c/16).toString(16)+
+(c%16).toString(16);})+'"';}
+return'"'+string+'"';},'number':function(number){return isFinite(number)?String(number):"null";},'boolean':function(bool){return String(bool);},'date':function(date){function format(number){return(number<10)?'0'+number:number;}
+return'"'+date.getFullYear()+'-'+
+format(date.getMonth()+1)+'-'+
+format(date.getDate())+'T'+
+format(date.getHours())+':'+
+format(date.getMinutes())+':'+
+format(date.getSeconds())+'"';}},CLASS_NAME:"OpenLayers.Format.JSON"});OpenLayers.Geometry=OpenLayers.Class({id:null,parent:null,bounds:null,initialize:function(){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){this.id=null;this.bounds=null;},clone:function(){return new OpenLayers.Geometry();},setBounds:function(bounds){if(bounds){this.bounds=bounds.clone();}},clearBounds:function(){this.bounds=null;if(this.parent){this.parent.clearBounds();}},extendBounds:function(newBounds){var bounds=this.getBounds();if(!bounds){this.setBounds(newBounds);}else{this.bounds.extend(newBounds);}},getBounds:function(){if(this.bounds==null){this.calculateBounds();}
 return this.bounds;},calculateBounds:function(){},distanceTo:function(geometry,options){},getVertices:function(nodes){},atPoint:function(lonlat,toleranceLon,toleranceLat){var atPoint=false;var bounds=this.getBounds();if((bounds!=null)&&(lonlat!=null)){var dX=(toleranceLon!=null)?toleranceLon:0;var dY=(toleranceLat!=null)?toleranceLat:0;var toleranceBounds=new OpenLayers.Bounds(this.bounds.left-dX,this.bounds.bottom-dY,this.bounds.right+dX,this.bounds.top+dY);atPoint=toleranceBounds.containsLonLat(lonlat);}
 return atPoint;},getLength:function(){return 0.0;},getArea:function(){return 0.0;},getCentroid:function(){return null;},toString:function(){var string;if(OpenLayers.Format&&OpenLayers.Format.WKT){string=OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this));}else{string=Object.prototype.toString.call(this);}
 return string;},CLASS_NAME:"OpenLayers.Geometry"});OpenLayers.Geometry.fromWKT=function(wkt){var geom;if(OpenLayers.Format&&OpenLayers.Format.WKT){var format=OpenLayers.Geometry.fromWKT.format;if(!format){format=new OpenLayers.Format.WKT();OpenLayers.Geometry.fromWKT.format=format;}
@@ -703,7 +726,7 @@ for(var i=0;i<features.length;++i){delete this.features[features[i].id];}
 this.redraw();},redraw:function(){if(!this.locked){var height=this.root.height;var width=this.root.width;this.canvas.clearRect(0,0,width,height);if(this.hitDetection){this.hitContext.clearRect(0,0,width,height);}
 var labelMap=[];var feature,geometry,style;var worldBounds=(this.map.baseLayer&&this.map.baseLayer.wrapDateLine)&&this.map.getMaxExtent();for(var id in this.features){if(!this.features.hasOwnProperty(id)){continue;}
 feature=this.features[id][0];geometry=feature.geometry;this.calculateFeatureDx(geometry.getBounds(),worldBounds);style=this.features[id][1];this.drawGeometry(geometry,style,feature.id);if(style.label){labelMap.push([feature,style]);}}
-var item;for(var i=0,len=labelMap.length;i<len;++i){item=labelMap[i];this.drawText(item[0].geometry.getCentroid(),item[1]);}}},CLASS_NAME:"OpenLayers.Renderer.Canvas"});OpenLayers.Renderer.Canvas.LABEL_ALIGN={"l":"left","r":"right","t":"top","b":"bottom"};OpenLayers.Renderer.Canvas.LABEL_FACTOR={"l":0,"r":-1,"t":0,"b":-1};OpenLayers.Renderer.Canvas.drawImageScaleFactor=null;OpenLayers.Format=OpenLayers.Class({options:null,externalProjection:null,internalProjection:null,data:null,keepData:false,initialize:function(options){OpenLayers.Util.extend(this,options);this.options=options;},destroy:function(){},read:function(data){throw new Error('Read not implemented.');},write:function(object){throw new Error('Write not implemented.');},CLASS_NAME:"OpenLayers.Format"});OpenLayers.Format.XML=OpenLayers.Class(OpenLayers.Format,{namespaces:null,namespaceAlias:null,defaultPrefix:null,readers:{},writers:{},xmldom:null,initialize:function(options){if(window.ActiveXObject){this.xmldom=new ActiveXObject("Microsoft.XMLDOM");}
+var item;for(var i=0,len=labelMap.length;i<len;++i){item=labelMap[i];this.drawText(item[0].geometry.getCentroid(),item[1]);}}},CLASS_NAME:"OpenLayers.Renderer.Canvas"});OpenLayers.Renderer.Canvas.LABEL_ALIGN={"l":"left","r":"right","t":"top","b":"bottom"};OpenLayers.Renderer.Canvas.LABEL_FACTOR={"l":0,"r":-1,"t":0,"b":-1};OpenLayers.Renderer.Canvas.drawImageScaleFactor=null;OpenLayers.Format.XML=OpenLayers.Class(OpenLayers.Format,{namespaces:null,namespaceAlias:null,defaultPrefix:null,readers:{},writers:{},xmldom:null,initialize:function(options){if(window.ActiveXObject){this.xmldom=new ActiveXObject("Microsoft.XMLDOM");}
 OpenLayers.Format.prototype.initialize.apply(this,[options]);this.namespaces=OpenLayers.Util.extend({},this.namespaces);this.namespaceAlias={};for(var alias in this.namespaces){this.namespaceAlias[this.namespaces[alias]]=alias;}},destroy:function(){this.xmldom=null;OpenLayers.Format.prototype.destroy.apply(this,arguments);},setNamespace:function(alias,uri){this.namespaces[alias]=uri;this.namespaceAlias[uri]=alias;},read:function(text){var index=text.indexOf('<');if(index>0){text=text.substring(index);}
 var node=OpenLayers.Util.Try(OpenLayers.Function.bind((function(){var xmldom;if(window.ActiveXObject&&!this.xmldom){xmldom=new ActiveXObject("Microsoft.XMLDOM");}else{xmldom=this.xmldom;}
 xmldom.loadXML(text);return xmldom;}),this),function(){return new DOMParser().parseFromString(text,'text/xml');},function(){var req=new XMLHttpRequest();req.open("GET","data:"+"text/xml"+";charset=utf-8,"+encodeURIComponent(text),false);if(req.overrideMimeType){req.overrideMimeType("text/xml");}
@@ -809,7 +832,22 @@ if(e.wheelDelta){delta=e.wheelDelta/120;if(window.opera&&window.opera.version()<
 this.delta=this.delta+delta;if(this.interval){window.clearTimeout(this._timeoutId);this._timeoutId=window.setTimeout(OpenLayers.Function.bind(function(){this.wheelZoom(e);},this),this.interval);}else{this.wheelZoom(e);}}
 OpenLayers.Event.stop(e);}},wheelZoom:function(e){var delta=this.delta;this.delta=0;if(delta){if(this.mousePosition){e.xy=this.mousePosition;}
 if(!e.xy){e.xy=this.map.getPixelFromLonLat(this.map.getCenter());}
-if(delta<0){this.callback("down",[e,this.cumulative?delta:-1]);}else{this.callback("up",[e,this.cumulative?delta:1]);}}},mousemove:function(evt){this.mousePosition=evt.xy;},activate:function(evt){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.observe(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.observe(window,"mousewheel",wheelListener);OpenLayers.Event.observe(document,"mousewheel",wheelListener);return true;}else{return false;}},deactivate:function(evt){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.stopObserving(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.stopObserving(window,"mousewheel",wheelListener);OpenLayers.Event.stopObserving(document,"mousewheel",wheelListener);return true;}else{return false;}},CLASS_NAME:"OpenLayers.Handler.MouseWheel"});OpenLayers.Lang.en={'unhandledRequest':"Unhandled request return ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Base Layer",'noFID':"Can't update a feature for which there is no FID.",'browserNotSupported':"Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",'minZoomLevelError':"The minZoomLevel property is only intended for use "+"with the FixedZoomLevels-descendent layers. That this "+"wfs layer checks for minZoomLevel is a relic of the"+"past. We cannot, however, remove it without possibly "+"breaking OL based applications that may depend on it."+" Therefore we are deprecating it -- the minZoomLevel "+"check below will be removed at 3.0. Please instead "+"use min/max resolution setting as described here: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaction: SUCCESS ${response}",'commitFailed':"WFS Transaction: FAILED ${response}",'googleWarning':"The Google Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the Google Maps library "+"script was either not included, or does not contain the "+"correct API key for your site.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>click here</a>",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the ${layerLib} library "+"script was not correctly included.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>click here</a>",'Scale = 1 : ${scaleDenom}':"Scale = 1 : ${scaleDenom}",'W':'W','E':'E','N':'N','S':'S','Graticule':'Graticule','reprojectDeprecated':"You are using the 'reproject' option "+"on the ${layerName} layer. This option is deprecated: "+"its use was designed to support displaying data over commercial "+"basemaps, but that functionality should now be achieved by using "+"Spherical Mercator support. More information is available from "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"This method has been deprecated and will be removed in 3.0. "+"Please use ${newMethod} instead.",'proxyNeeded':"You probably need to set OpenLayers.ProxyHost to access ${url}."+"See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost",'end':''};OpenLayers.Lang['en-CA']=OpenLayers.Util.applyDefaults({},OpenLayers.Lang["en"]);OpenLayers.Popup=OpenLayers.Class({events:null,id:"",lonlat:null,div:null,contentSize:null,size:null,contentHTML:null,backgroundColor:"",opacity:"",border:"",contentDiv:null,groupDiv:null,closeDiv:null,autoSize:false,minSize:null,maxSize:null,displayClass:"olPopup",contentDisplayClass:"olPopupContent",padding:0,disableFirefoxOverflowHack:false,fixPadding:function(){if(typeof this.padding=="number"){this.padding=new OpenLayers.Bounds(this.padding,this.padding,this.padding,this.padding);}},panMapIfOutOfView:false,keepInMap:false,closeOnMove:false,map:null,initialize:function(id,lonlat,contentSize,contentHTML,closeBox,closeBoxCallback){if(id==null){id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");}
+if(delta<0){this.callback("down",[e,this.cumulative?delta:-1]);}else{this.callback("up",[e,this.cumulative?delta:1]);}}},mousemove:function(evt){this.mousePosition=evt.xy;},activate:function(evt){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.observe(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.observe(window,"mousewheel",wheelListener);OpenLayers.Event.observe(document,"mousewheel",wheelListener);return true;}else{return false;}},deactivate:function(evt){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.stopObserving(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.stopObserving(window,"mousewheel",wheelListener);OpenLayers.Event.stopObserving(document,"mousewheel",wheelListener);return true;}else{return false;}},CLASS_NAME:"OpenLayers.Handler.MouseWheel"});OpenLayers.Lang.en={'unhandledRequest':"Unhandled request return ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Base Layer",'noFID':"Can't update a feature for which there is no FID.",'browserNotSupported':"Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",'minZoomLevelError':"The minZoomLevel property is only intended for use "+"with the FixedZoomLevels-descendent layers. That this "+"wfs layer checks for minZoomLevel is a relic of the"+"past. We cannot, however, remove it without possibly "+"breaking OL based applications that may depend on it."+" Therefore we are deprecating it -- the minZoomLevel "+"check below will be removed at 3.0. Please instead "+"use min/max resolution setting as described here: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaction: SUCCESS ${response}",'commitFailed':"WFS Transaction: FAILED ${response}",'googleWarning':"The Google Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the Google Maps library "+"script was either not included, or does not contain the "+"correct API key for your site.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>click here</a>",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the ${layerLib} library "+"script was not correctly included.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>click here</a>",'Scale = 1 : ${scaleDenom}':"Scale = 1 : ${scaleDenom}",'W':'W','E':'E','N':'N','S':'S','Graticule':'Graticule','reprojectDeprecated':"You are using the 'reproject' option "+"on the ${layerName} layer. This option is deprecated: "+"its use was designed to support displaying data over commercial "+"basemaps, but that functionality should now be achieved by using "+"Spherical Mercator support. More information is available from "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"This method has been deprecated and will be removed in 3.0. "+"Please use ${newMethod} instead.",'proxyNeeded':"You probably need to set OpenLayers.ProxyHost to access ${url}."+"See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost",'end':''};OpenLayers.Lang['en-CA']=OpenLayers.Util.applyDefaults({},OpenLayers.Lang["en"]);OpenLayers.Geometry.MultiLineString=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.LineString"],split:function(geometry,options){var results=null;var mutual=options&&options.mutual;var splits,sourceLine,sourceLines,sourceSplit,targetSplit;var sourceParts=[];var targetParts=[geometry];for(var i=0,len=this.components.length;i<len;++i){sourceLine=this.components[i];sourceSplit=false;for(var j=0;j<targetParts.length;++j){splits=sourceLine.split(targetParts[j],options);if(splits){if(mutual){sourceLines=splits[0];for(var k=0,klen=sourceLines.length;k<klen;++k){if(k===0&&sourceParts.length){sourceParts[sourceParts.length-1].addComponent(sourceLines[k]);}else{sourceParts.push(new OpenLayers.Geometry.MultiLineString([sourceLines[k]]));}}
+sourceSplit=true;splits=splits[1];}
+if(splits.length){splits.unshift(j,1);Array.prototype.splice.apply(targetParts,splits);break;}}}
+if(!sourceSplit){if(sourceParts.length){sourceParts[sourceParts.length-1].addComponent(sourceLine.clone());}else{sourceParts=[new OpenLayers.Geometry.MultiLineString(sourceLine.clone())];}}}
+if(sourceParts&&sourceParts.length>1){sourceSplit=true;}else{sourceParts=[];}
+if(targetParts&&targetParts.length>1){targetSplit=true;}else{targetParts=[];}
+if(sourceSplit||targetSplit){if(mutual){results=[sourceParts,targetParts];}else{results=targetParts;}}
+return results;},splitWith:function(geometry,options){var results=null;var mutual=options&&options.mutual;var splits,targetLine,sourceLines,sourceSplit,targetSplit,sourceParts,targetParts;if(geometry instanceof OpenLayers.Geometry.LineString){targetParts=[];sourceParts=[geometry];for(var i=0,len=this.components.length;i<len;++i){targetSplit=false;targetLine=this.components[i];for(var j=0;j<sourceParts.length;++j){splits=sourceParts[j].split(targetLine,options);if(splits){if(mutual){sourceLines=splits[0];if(sourceLines.length){sourceLines.unshift(j,1);Array.prototype.splice.apply(sourceParts,sourceLines);j+=sourceLines.length-2;}
+splits=splits[1];if(splits.length===0){splits=[targetLine.clone()];}}
+for(var k=0,klen=splits.length;k<klen;++k){if(k===0&&targetParts.length){targetParts[targetParts.length-1].addComponent(splits[k]);}else{targetParts.push(new OpenLayers.Geometry.MultiLineString([splits[k]]));}}
+targetSplit=true;}}
+if(!targetSplit){if(targetParts.length){targetParts[targetParts.length-1].addComponent(targetLine.clone());}else{targetParts=[new OpenLayers.Geometry.MultiLineString([targetLine.clone()])];}}}}else{results=geometry.split(this);}
+if(sourceParts&&sourceParts.length>1){sourceSplit=true;}else{sourceParts=[];}
+if(targetParts&&targetParts.length>1){targetSplit=true;}else{targetParts=[];}
+if(sourceSplit||targetSplit){if(mutual){results=[sourceParts,targetParts];}else{results=targetParts;}}
+return results;},CLASS_NAME:"OpenLayers.Geometry.MultiLineString"});OpenLayers.Popup=OpenLayers.Class({events:null,id:"",lonlat:null,div:null,contentSize:null,size:null,contentHTML:null,backgroundColor:"",opacity:"",border:"",contentDiv:null,groupDiv:null,closeDiv:null,autoSize:false,minSize:null,maxSize:null,displayClass:"olPopup",contentDisplayClass:"olPopupContent",padding:0,disableFirefoxOverflowHack:false,fixPadding:function(){if(typeof this.padding=="number"){this.padding=new OpenLayers.Bounds(this.padding,this.padding,this.padding,this.padding);}},panMapIfOutOfView:false,keepInMap:false,closeOnMove:false,map:null,initialize:function(id,lonlat,contentSize,contentHTML,closeBox,closeBoxCallback){if(id==null){id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");}
 this.id=id;this.lonlat=lonlat;this.contentSize=(contentSize!=null)?contentSize:new OpenLayers.Size(OpenLayers.Popup.WIDTH,OpenLayers.Popup.HEIGHT);if(contentHTML!=null){this.contentHTML=contentHTML;}
 this.backgroundColor=OpenLayers.Popup.COLOR;this.opacity=OpenLayers.Popup.OPACITY;this.border=OpenLayers.Popup.BORDER;this.div=OpenLayers.Util.createDiv(this.id,null,null,null,null,null,"hidden");this.div.className=this.displayClass;var groupDivId=this.id+"_GroupDiv";this.groupDiv=OpenLayers.Util.createDiv(groupDivId,null,null,null,"relative",null,"hidden");var id=this.div.id+"_contentDiv";this.contentDiv=OpenLayers.Util.createDiv(id,null,this.contentSize.clone(),null,"relative");this.contentDiv.className=this.contentDisplayClass;this.groupDiv.appendChild(this.contentDiv);this.div.appendChild(this.groupDiv);if(closeBox){this.addCloseBox(closeBoxCallback);}
 this.registerEvents();},destroy:function(){this.id=null;this.lonlat=null;this.size=null;this.contentHTML=null;this.backgroundColor=null;this.opacity=null;this.border=null;if(this.closeOnMove&&this.map){this.map.events.unregister("movestart",this,this.hide);}
@@ -902,21 +940,7 @@ return maxExtent;},CLASS_NAME:"OpenLayers.Layer.Markers"});OpenLayers.Control.Zo
 eventsInstance.register("buttonclick",this,this.onZoomClick);this.zoomInLink=zoomIn;this.zoomOutLink=zoomOut;return div;},getOrCreateLinks:function(el){var zoomIn=document.getElementById(this.zoomInId),zoomOut=document.getElementById(this.zoomOutId);if(!zoomIn){zoomIn=document.createElement("a");zoomIn.href="#zoomIn";zoomIn.appendChild(document.createTextNode(this.zoomInText));zoomIn.className="olControlZoomIn";el.appendChild(zoomIn);}
 OpenLayers.Element.addClass(zoomIn,"olButton");if(!zoomOut){zoomOut=document.createElement("a");zoomOut.href="#zoomOut";zoomOut.appendChild(document.createTextNode(this.zoomOutText));zoomOut.className="olControlZoomOut";el.appendChild(zoomOut);}
 OpenLayers.Element.addClass(zoomOut,"olButton");return{zoomIn:zoomIn,zoomOut:zoomOut};},onZoomClick:function(evt){var button=evt.buttonElement;if(button===this.zoomInLink){this.map.zoomIn();}else if(button===this.zoomOutLink){this.map.zoomOut();}},destroy:function(){if(this.map){this.map.events.unregister("buttonclick",this,this.onZoomClick);}
-delete this.zoomInLink;delete this.zoomOutLink;OpenLayers.Control.prototype.destroy.apply(this);},CLASS_NAME:"OpenLayers.Control.Zoom"});OpenLayers.Lang.it={'unhandledRequest':"Codice di ritorno della richiesta ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Livello base",'noFID':"Impossibile aggiornare un elemento grafico che non abbia il FID.",'browserNotSupported':"Il tuo browser non supporta il rendering vettoriale. I renderizzatore attualemnte supportati sono:\n${renderers}",'minZoomLevelError':"La proprietà minZoomLevel è da utilizzare solamente "+"con livelli che abbiano FixedZoomLevels. Il fatto che "+"questo livello wfs controlli la proprietà minZoomLevel è "+"un retaggio del passato. Non possiamo comunque rimuoverla "+"senza rompere le vecchie applicazioni che dipendono su di essa."+"Quindi siamo costretti a deprecarla -- minZoomLevel "+"e sarà rimossa dalla vesione 3.0. Si prega di utilizzare i "+"settaggi di risoluzione min/max come descritto qui: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transazione WFS: SUCCESS ${response}",'commitFailed':"Transazione WFS: FAILED ${response}",'googleWarning':"Il livello Google non è riuscito a caricare correttamente.<br><br>"+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.<br><br>"+"Più precisamente, ciò accade perchè la libreria Google Maps "+"non è stata inclusa nella pagina, oppure non contiene la "+"corretta API key per il tuo sito.<br><br>"+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>clicca qui</a>",'getLayerWarning':"Il livello ${layerType} non è riuscito a caricare correttamente.<br><br>"+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.<br><br>"+"Più precisamente, ciò accade perchè la libreria ${layerLib} "+"non è stata inclusa nella pagina.<br><br>"+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>clicca qui</a>",'Scale = 1 : ${scaleDenom}':"Scala = 1 : ${scaleDenom}",'reprojectDeprecated':"Stai utilizzando l'opzione 'reproject' sul livello ${layerName}. "+"Questa opzione è deprecata: il suo utilizzo è stato introdotto per"+"supportare il disegno dei dati sopra mappe commerciali, ma tale "+"funzionalità dovrebbe essere ottenuta tramite l'utilizzo della proiezione "+"Spherical Mercator. Per maggiori informazioni consultare qui "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Questo metodo è stato deprecato e sarà rimosso dalla versione 3.0. "+"Si prega di utilizzare il metodo ${newMethod} in alternativa.",'end':''};OpenLayers.Lang["oc"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Requèsta pas gerida, retorna ${statusText}",'Permalink':"Permaligam",'Overlays':"Calques",'Base Layer':"Calc de basa",'noFID':"Impossible de metre a jorn un objècte sens identificant (fid).",'browserNotSupported':"Vòstre navegidor supòrta pas lo rendut vectorial. Los renderers actualament suportats son : \n${renderers}",'minZoomLevelError':"La proprietat minZoomLevel deu èsser utilizada solament per de jaces FixedZoomLevels-descendent. Lo fach qu\'aqueste jaç WFS verifique la preséncia de minZoomLevel es una relica del passat. Çaquelà, la podèm suprimir sens copar d\'aplicacions que ne poirián dependre. Es per aquò que la depreciam -- la verificacion del minZoomLevel serà suprimida en version 3.0. A la plaça, mercés d\'utilizar los paramètres de resolucions min/max tal coma descrich sus : http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transaccion WFS : SUCCES ${response}",'commitFailed':"Transaccion WFS : FRACAS ${response}",'googleWarning':"Lo jaç Google es pas estat en mesura de se cargar corrèctament.\x3cbr\x3e\x3cbr\x3ePer suprimir aqueste messatge, causissètz una BaseLayer novèla dins lo selector de jaç en naut a drecha.\x3cbr\x3e\x3cbr\x3eAquò es possiblament causat par la non-inclusion de la librariá Google Maps, o alara perque que la clau de l\'API correspond pas a vòstre site.\x3cbr\x3e\x3cbr\x3eDesvolopaires : per saber cossí corregir aquò, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eclicatz aicí\x3c/a\x3e",'getLayerWarning':"Lo jaç ${layerType} es pas en mesura de se cargar corrèctament.\x3cbr\x3e\x3cbr\x3ePer suprimir aqueste messatge, causissètz una  BaseLayer novèla dins lo selector de jaç en naut a drecha.\x3cbr\x3e\x3cbr\x3eAquò es possiblament causat per la non-inclusion de la librariá ${layerLib}.\x3cbr\x3e\x3cbr\x3eDesvolopaires : per saber cossí corregir aquí, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eclicatz aicí\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Escala ~ 1 : ${scaleDenom}",'W':"O",'E':"È",'N':"N",'S':"S",'reprojectDeprecated':"Utilizatz l\'opcion \'reproject\' sul jaç ${layerName}. Aquesta opcion es despreciada : Son usatge permetiá d\'afichar de donadas al dessús de jaces raster comercials. Aquesta foncionalitat ara es suportada en utilizant lo supòrt de la projeccion Mercator Esferica. Mai d\'informacion es disponibla sus http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Aqueste metòde es despreciada, e serà suprimida a la version 3.0. Mercés d\'utilizar ${newMethod} a la plaça."});OpenLayers.Lang["gsw"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Nit behandleti Aafrogsruckmäldig ${statusText}",'Permalink':"Permalink",'Overlays':"Iberlagerige",'Base Layer':"Grundcharte",'noFID':"E Feature, wu s kei FID derfir git, cha nit aktualisiert wäre.",'browserNotSupported':"Dyy Browser unterstitzt kei Vektordarstellig. Aktuäll unterstitzti Renderer:\n${renderers}",'minZoomLevelError':"D minZoomLevel-Eigeschaft isch nume dänk fir d Layer, wu vu dr FixedZoomLevels abstamme. Ass dää wfs-Layer minZoomLevel prieft, scih e Relikt us dr Vergangeheit. Mir chenne s aber nit ändere ohni OL_basierti Aawändige villicht kaputt gehn, wu dervu abhänge.  Us däm Grund het die Funktion d Eigeschaft \'deprecated\' iberchuu. D minZoomLevel-Priefig unte wird in dr Version 3.0 usegnuu. Bitte verwänd statt däm e min/max-Uflesig wie s do bschriben isch: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS-Transaktion: ERFOLGRYCH ${response}",'commitFailed':"WFS-Transaktion: FÄHLGSCHLAA ${response}",'googleWarning':"Dr Google-Layer het nit korräkt chenne glade wäre.\x3cbr\x3e\x3cbr\x3eGo die Mäldig nimi z kriege, wehl e andere Hintergrundlayer us em LayerSwitcher im rächte obere Ecke.\x3cbr\x3e\x3cbr\x3eDää Fähler git s seli hyfig, wel s Skript vu dr Google-Maps-Bibliothek nit yybunde woren isch oder wel s kei giltige API-Schlissel fir Dyy URL din het.\x3cbr\x3e\x3cbr\x3eEntwickler: Fir Hilf zum korräkte Yybinde vum Google-Layer \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3edoo drucke\x3c/a\x3e",'getLayerWarning':"Dr ${layerType}-Layer het nit korräkt chenne glade wäre.\x3cbr\x3e\x3cbr\x3eGo die Mäldig nimi z kriege, wehl e andere Hintergrundlayer us em LayerSwitcher im rächte obere Ecke.\x3cbr\x3e\x3cbr\x3eDää Fähler git s seli hyfig, wel s Skript vu dr \'${layerLib}\'-Bibliothek nit yybunde woren isch oder wel s kei giltige API-Schlissel fir Dyy URL din het.\x3cbr\x3e\x3cbr\x3eEntwickler: Fir Hilf zum korräkte Yybinde vu Layer \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3edoo drucke\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Maßstab = 1 : ${scaleDenom}",'W':"W",'E':"O",'N':"N",'S':"S",'reprojectDeprecated':"Du bruchsch d \'reproject\'-Option bim ${layerName}-Layer. Die Option isch nimi giltig: si isch aagleit wore go   Date iber kommerziälli Grundcharte lege, aber des sott mer jetz mache mit dr Unterstitzig vu Spherical Mercator. Meh Informatione git s uf http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Die Methode isch veraltet un wird us dr Version 3.0 usegnuu. Bitte verwäbnd statt däm ${newMethod}."});OpenLayers.Handler.Feature=OpenLayers.Class(OpenLayers.Handler,{EVENTMAP:{'click':{'in':'click','out':'clickout'},'mousemove':{'in':'over','out':'out'},'dblclick':{'in':'dblclick','out':null},'mousedown':{'in':null,'out':null},'mouseup':{'in':null,'out':null},'touchstart':{'in':'click','out':'clickout'}},feature:null,lastFeature:null,down:null,up:null,touch:false,clickTolerance:4,geometryTypes:null,stopClick:true,stopDown:true,stopUp:false,initialize:function(control,layer,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,[control,callbacks,options]);this.layer=layer;},touchstart:function(evt){if(!this.touch){this.touch=true;this.map.events.un({mousedown:this.mousedown,mouseup:this.mouseup,mousemove:this.mousemove,click:this.click,dblclick:this.dblclick,scope:this});}
-return OpenLayers.Event.isMultiTouch(evt)?true:this.mousedown(evt);},touchmove:function(evt){OpenLayers.Event.stop(evt);},mousedown:function(evt){if(OpenLayers.Event.isLeftClick(evt)||OpenLayers.Event.isSingleTouch(evt)){this.down=evt.xy;}
-return this.handle(evt)?!this.stopDown:true;},mouseup:function(evt){this.up=evt.xy;return this.handle(evt)?!this.stopUp:true;},click:function(evt){return this.handle(evt)?!this.stopClick:true;},mousemove:function(evt){if(!this.callbacks['over']&&!this.callbacks['out']){return true;}
-this.handle(evt);return true;},dblclick:function(evt){return!this.handle(evt);},geometryTypeMatches:function(feature){return this.geometryTypes==null||OpenLayers.Util.indexOf(this.geometryTypes,feature.geometry.CLASS_NAME)>-1;},handle:function(evt){if(this.feature&&!this.feature.layer){this.feature=null;}
-var type=evt.type;var handled=false;var previouslyIn=!!(this.feature);var click=(type=="click"||type=="dblclick"||type=="touchstart");this.feature=this.layer.getFeatureFromEvent(evt);if(this.feature&&!this.feature.layer){this.feature=null;}
-if(this.lastFeature&&!this.lastFeature.layer){this.lastFeature=null;}
-if(this.feature){if(type==="touchstart"){OpenLayers.Event.stop(evt);}
-var inNew=(this.feature!=this.lastFeature);if(this.geometryTypeMatches(this.feature)){if(previouslyIn&&inNew){if(this.lastFeature){this.triggerCallback(type,'out',[this.lastFeature]);}
-this.triggerCallback(type,'in',[this.feature]);}else if(!previouslyIn||click){this.triggerCallback(type,'in',[this.feature]);}
-this.lastFeature=this.feature;handled=true;}else{if(this.lastFeature&&(previouslyIn&&inNew||click)){this.triggerCallback(type,'out',[this.lastFeature]);}
-this.feature=null;}}else{if(this.lastFeature&&(previouslyIn||click)){this.triggerCallback(type,'out',[this.lastFeature]);}}
-return handled;},triggerCallback:function(type,mode,args){var key=this.EVENTMAP[type][mode];if(key){if(type=='click'&&this.up&&this.down){var dpx=Math.sqrt(Math.pow(this.up.x-this.down.x,2)+
-Math.pow(this.up.y-this.down.y,2));if(dpx<=this.clickTolerance){this.callback(key,args);}}else{this.callback(key,args);}}},activate:function(){var activated=false;if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.moveLayerToTop();this.map.events.on({"removelayer":this.handleMapEvents,"changelayer":this.handleMapEvents,scope:this});activated=true;}
-return activated;},deactivate:function(){var deactivated=false;if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){this.moveLayerBack();this.feature=null;this.lastFeature=null;this.down=null;this.up=null;this.touch=false;this.map.events.un({"removelayer":this.handleMapEvents,"changelayer":this.handleMapEvents,scope:this});deactivated=true;}
-return deactivated;},handleMapEvents:function(evt){if(evt.type=="removelayer"||evt.property=="order"){this.moveLayerToTop();}},moveLayerToTop:function(){var index=Math.max(this.map.Z_INDEX_BASE['Feature']-1,this.layer.getZIndex())+1;this.layer.setZIndex(index);},moveLayerBack:function(){var index=this.layer.getZIndex()-1;if(index>=this.map.Z_INDEX_BASE['Feature']){this.layer.setZIndex(index);}else{this.map.setLayerZIndex(this.layer,this.map.getLayerIndex(this.layer));}},CLASS_NAME:"OpenLayers.Handler.Feature"});OpenLayers.Style=OpenLayers.Class({id:null,name:null,title:null,description:null,layerName:null,isDefault:false,rules:null,context:null,defaultStyle:null,defaultsPerSymbolizer:false,propertyStyles:null,initialize:function(style,options){OpenLayers.Util.extend(this,options);this.rules=[];if(options&&options.rules){this.addRules(options.rules);}
+delete this.zoomInLink;delete this.zoomOutLink;OpenLayers.Control.prototype.destroy.apply(this);},CLASS_NAME:"OpenLayers.Control.Zoom"});OpenLayers.Lang.it={'unhandledRequest':"Codice di ritorno della richiesta ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Livello base",'noFID':"Impossibile aggiornare un elemento grafico che non abbia il FID.",'browserNotSupported':"Il tuo browser non supporta il rendering vettoriale. I renderizzatore attualemnte supportati sono:\n${renderers}",'minZoomLevelError':"La proprietà minZoomLevel è da utilizzare solamente "+"con livelli che abbiano FixedZoomLevels. Il fatto che "+"questo livello wfs controlli la proprietà minZoomLevel è "+"un retaggio del passato. Non possiamo comunque rimuoverla "+"senza rompere le vecchie applicazioni che dipendono su di essa."+"Quindi siamo costretti a deprecarla -- minZoomLevel "+"e sarà rimossa dalla vesione 3.0. Si prega di utilizzare i "+"settaggi di risoluzione min/max come descritto qui: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transazione WFS: SUCCESS ${response}",'commitFailed':"Transazione WFS: FAILED ${response}",'googleWarning':"Il livello Google non è riuscito a caricare correttamente.<br><br>"+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.<br><br>"+"Più precisamente, ciò accade perchè la libreria Google Maps "+"non è stata inclusa nella pagina, oppure non contiene la "+"corretta API key per il tuo sito.<br><br>"+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>clicca qui</a>",'getLayerWarning':"Il livello ${layerType} non è riuscito a caricare correttamente.<br><br>"+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.<br><br>"+"Più precisamente, ciò accade perchè la libreria ${layerLib} "+"non è stata inclusa nella pagina.<br><br>"+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>clicca qui</a>",'Scale = 1 : ${scaleDenom}':"Scala = 1 : ${scaleDenom}",'reprojectDeprecated':"Stai utilizzando l'opzione 'reproject' sul livello ${layerName}. "+"Questa opzione è deprecata: il suo utilizzo è stato introdotto per"+"supportare il disegno dei dati sopra mappe commerciali, ma tale "+"funzionalità dovrebbe essere ottenuta tramite l'utilizzo della proiezione "+"Spherical Mercator. Per maggiori informazioni consultare qui "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Questo metodo è stato deprecato e sarà rimosso dalla versione 3.0. "+"Si prega di utilizzare il metodo ${newMethod} in alternativa.",'end':''};OpenLayers.Lang["oc"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Requèsta pas gerida, retorna ${statusText}",'Permalink':"Permaligam",'Overlays':"Calques",'Base Layer':"Calc de basa",'noFID':"Impossible de metre a jorn un objècte sens identificant (fid).",'browserNotSupported':"Vòstre navegidor supòrta pas lo rendut vectorial. Los renderers actualament suportats son : \n${renderers}",'minZoomLevelError':"La proprietat minZoomLevel deu èsser utilizada solament per de jaces FixedZoomLevels-descendent. Lo fach qu\'aqueste jaç WFS verifique la preséncia de minZoomLevel es una relica del passat. Çaquelà, la podèm suprimir sens copar d\'aplicacions que ne poirián dependre. Es per aquò que la depreciam -- la verificacion del minZoomLevel serà suprimida en version 3.0. A la plaça, mercés d\'utilizar los paramètres de resolucions min/max tal coma descrich sus : http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transaccion WFS : SUCCES ${response}",'commitFailed':"Transaccion WFS : FRACAS ${response}",'googleWarning':"Lo jaç Google es pas estat en mesura de se cargar corrèctament.\x3cbr\x3e\x3cbr\x3ePer suprimir aqueste messatge, causissètz una BaseLayer novèla dins lo selector de jaç en naut a drecha.\x3cbr\x3e\x3cbr\x3eAquò es possiblament causat par la non-inclusion de la librariá Google Maps, o alara perque que la clau de l\'API correspond pas a vòstre site.\x3cbr\x3e\x3cbr\x3eDesvolopaires : per saber cossí corregir aquò, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eclicatz aicí\x3c/a\x3e",'getLayerWarning':"Lo jaç ${layerType} es pas en mesura de se cargar corrèctament.\x3cbr\x3e\x3cbr\x3ePer suprimir aqueste messatge, causissètz una  BaseLayer novèla dins lo selector de jaç en naut a drecha.\x3cbr\x3e\x3cbr\x3eAquò es possiblament causat per la non-inclusion de la librariá ${layerLib}.\x3cbr\x3e\x3cbr\x3eDesvolopaires : per saber cossí corregir aquí, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eclicatz aicí\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Escala ~ 1 : ${scaleDenom}",'W':"O",'E':"È",'N':"N",'S':"S",'reprojectDeprecated':"Utilizatz l\'opcion \'reproject\' sul jaç ${layerName}. Aquesta opcion es despreciada : Son usatge permetiá d\'afichar de donadas al dessús de jaces raster comercials. Aquesta foncionalitat ara es suportada en utilizant lo supòrt de la projeccion Mercator Esferica. Mai d\'informacion es disponibla sus http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Aqueste metòde es despreciada, e serà suprimida a la version 3.0. Mercés d\'utilizar ${newMethod} a la plaça."});OpenLayers.Lang["gsw"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Nit behandleti Aafrogsruckmäldig ${statusText}",'Permalink':"Permalink",'Overlays':"Iberlagerige",'Base Layer':"Grundcharte",'noFID':"E Feature, wu s kei FID derfir git, cha nit aktualisiert wäre.",'browserNotSupported':"Dyy Browser unterstitzt kei Vektordarstellig. Aktuäll unterstitzti Renderer:\n${renderers}",'minZoomLevelError':"D minZoomLevel-Eigeschaft isch nume dänk fir d Layer, wu vu dr FixedZoomLevels abstamme. Ass dää wfs-Layer minZoomLevel prieft, scih e Relikt us dr Vergangeheit. Mir chenne s aber nit ändere ohni OL_basierti Aawändige villicht kaputt gehn, wu dervu abhänge.  Us däm Grund het die Funktion d Eigeschaft \'deprecated\' iberchuu. D minZoomLevel-Priefig unte wird in dr Version 3.0 usegnuu. Bitte verwänd statt däm e min/max-Uflesig wie s do bschriben isch: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS-Transaktion: ERFOLGRYCH ${response}",'commitFailed':"WFS-Transaktion: FÄHLGSCHLAA ${response}",'googleWarning':"Dr Google-Layer het nit korräkt chenne glade wäre.\x3cbr\x3e\x3cbr\x3eGo die Mäldig nimi z kriege, wehl e andere Hintergrundlayer us em LayerSwitcher im rächte obere Ecke.\x3cbr\x3e\x3cbr\x3eDää Fähler git s seli hyfig, wel s Skript vu dr Google-Maps-Bibliothek nit yybunde woren isch oder wel s kei giltige API-Schlissel fir Dyy URL din het.\x3cbr\x3e\x3cbr\x3eEntwickler: Fir Hilf zum korräkte Yybinde vum Google-Layer \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3edoo drucke\x3c/a\x3e",'getLayerWarning':"Dr ${layerType}-Layer het nit korräkt chenne glade wäre.\x3cbr\x3e\x3cbr\x3eGo die Mäldig nimi z kriege, wehl e andere Hintergrundlayer us em LayerSwitcher im rächte obere Ecke.\x3cbr\x3e\x3cbr\x3eDää Fähler git s seli hyfig, wel s Skript vu dr \'${layerLib}\'-Bibliothek nit yybunde woren isch oder wel s kei giltige API-Schlissel fir Dyy URL din het.\x3cbr\x3e\x3cbr\x3eEntwickler: Fir Hilf zum korräkte Yybinde vu Layer \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3edoo drucke\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Maßstab = 1 : ${scaleDenom}",'W':"W",'E':"O",'N':"N",'S':"S",'reprojectDeprecated':"Du bruchsch d \'reproject\'-Option bim ${layerName}-Layer. Die Option isch nimi giltig: si isch aagleit wore go   Date iber kommerziälli Grundcharte lege, aber des sott mer jetz mache mit dr Unterstitzig vu Spherical Mercator. Meh Informatione git s uf http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Die Methode isch veraltet un wird us dr Version 3.0 usegnuu. Bitte verwäbnd statt däm ${newMethod}."});OpenLayers.Geometry.MultiPolygon=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.Polygon"],CLASS_NAME:"OpenLayers.Geometry.MultiPolygon"});OpenLayers.Style=OpenLayers.Class({id:null,name:null,title:null,description:null,layerName:null,isDefault:false,rules:null,context:null,defaultStyle:null,defaultsPerSymbolizer:false,propertyStyles:null,initialize:function(style,options){OpenLayers.Util.extend(this,options);this.rules=[];if(options&&options.rules){this.addRules(options.rules);}
 this.setDefaultStyle(style||OpenLayers.Feature.Vector.style["default"]);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){for(var i=0,len=this.rules.length;i<len;i++){this.rules[i].destroy();this.rules[i]=null;}
 this.rules=null;this.defaultStyle=null;},createSymbolizer:function(feature){var style=this.defaultsPerSymbolizer?{}:this.createLiterals(OpenLayers.Util.extend({},this.defaultStyle),feature);var rules=this.rules;var rule,context;var elseRules=[];var appliedRules=false;for(var i=0,len=rules.length;i<len;i++){rule=rules[i];var applies=rule.evaluate(feature);if(applies){if(rule instanceof OpenLayers.Rule&&rule.elseFilter){elseRules.push(rule);}else{appliedRules=true;this.applySymbolizer(rule,style,feature);}}}
 if(appliedRules==false&&elseRules.length>0){appliedRules=true;for(var i=0,len=elseRules.length;i<len;i++){this.applySymbolizer(elseRules[i],style,feature);}}
@@ -930,7 +954,36 @@ return style;},findPropertyStyles:function(){var propertyStyles={};var style=thi
 return propertyStyles;},addPropertyStyles:function(propertyStyles,symbolizer){var property;for(var key in symbolizer){property=symbolizer[key];if(typeof property=="string"&&property.match(/\$\{\w+\}/)){propertyStyles[key]=true;}}
 return propertyStyles;},addRules:function(rules){Array.prototype.push.apply(this.rules,rules);this.propertyStyles=this.findPropertyStyles();},setDefaultStyle:function(style){this.defaultStyle=style;this.propertyStyles=this.findPropertyStyles();},getSymbolizerPrefix:function(geometry){var prefixes=OpenLayers.Style.SYMBOLIZER_PREFIXES;for(var i=0,len=prefixes.length;i<len;i++){if(geometry.CLASS_NAME.indexOf(prefixes[i])!=-1){return prefixes[i];}}},clone:function(){var options=OpenLayers.Util.extend({},this);if(this.rules){options.rules=[];for(var i=0,len=this.rules.length;i<len;++i){options.rules.push(this.rules[i].clone());}}
 options.context=this.context&&OpenLayers.Util.extend({},this.context);var defaultStyle=OpenLayers.Util.extend({},this.defaultStyle);return new OpenLayers.Style(defaultStyle,options);},CLASS_NAME:"OpenLayers.Style"});OpenLayers.Style.createLiteral=function(value,context,feature,property){if(typeof value=="string"&&value.indexOf("${")!=-1){value=OpenLayers.String.format(value,context,[feature,property]);value=(isNaN(value)||!value)?value:parseFloat(value);}
-return value;};OpenLayers.Style.SYMBOLIZER_PREFIXES=['Point','Line','Polygon','Text','Raster'];OpenLayers.StyleMap=OpenLayers.Class({styles:null,extendDefault:true,initialize:function(style,options){this.styles={"default":new OpenLayers.Style(OpenLayers.Feature.Vector.style["default"]),"select":new OpenLayers.Style(OpenLayers.Feature.Vector.style["select"]),"temporary":new OpenLayers.Style(OpenLayers.Feature.Vector.style["temporary"]),"delete":new OpenLayers.Style(OpenLayers.Feature.Vector.style["delete"])};if(style instanceof OpenLayers.Style){this.styles["default"]=style;this.styles["select"]=style;this.styles["temporary"]=style;this.styles["delete"]=style;}else if(typeof style=="object"){for(var key in style){if(style[key]instanceof OpenLayers.Style){this.styles[key]=style[key];}else if(typeof style[key]=="object"){this.styles[key]=new OpenLayers.Style(style[key]);}else{this.styles["default"]=new OpenLayers.Style(style);this.styles["select"]=new OpenLayers.Style(style);this.styles["temporary"]=new OpenLayers.Style(style);this.styles["delete"]=new OpenLayers.Style(style);break;}}}
+return value;};OpenLayers.Style.SYMBOLIZER_PREFIXES=['Point','Line','Polygon','Text','Raster'];OpenLayers.Filter=OpenLayers.Class({initialize:function(options){OpenLayers.Util.extend(this,options);},destroy:function(){},evaluate:function(context){return true;},clone:function(){return null;},toString:function(){var string;if(OpenLayers.Format&&OpenLayers.Format.CQL){string=OpenLayers.Format.CQL.prototype.write(this);}else{string=Object.prototype.toString.call(this);}
+return string;},CLASS_NAME:"OpenLayers.Filter"});OpenLayers.Filter.Spatial=OpenLayers.Class(OpenLayers.Filter,{type:null,property:null,value:null,distance:null,distanceUnits:null,evaluate:function(feature){var intersect=false;switch(this.type){case OpenLayers.Filter.Spatial.BBOX:case OpenLayers.Filter.Spatial.INTERSECTS:if(feature.geometry){var geom=this.value;if(this.value.CLASS_NAME=="OpenLayers.Bounds"){geom=this.value.toGeometry();}
+if(feature.geometry.intersects(geom)){intersect=true;}}
+break;default:throw new Error('evaluate is not implemented for this filter type.');}
+return intersect;},clone:function(){var options=OpenLayers.Util.applyDefaults({value:this.value&&this.value.clone&&this.value.clone()},this);return new OpenLayers.Filter.Spatial(options);},CLASS_NAME:"OpenLayers.Filter.Spatial"});OpenLayers.Filter.Spatial.BBOX="BBOX";OpenLayers.Filter.Spatial.INTERSECTS="INTERSECTS";OpenLayers.Filter.Spatial.DWITHIN="DWITHIN";OpenLayers.Filter.Spatial.WITHIN="WITHIN";OpenLayers.Filter.Spatial.CONTAINS="CONTAINS";OpenLayers.Strategy.BBOX=OpenLayers.Class(OpenLayers.Strategy,{bounds:null,resolution:null,ratio:2,resFactor:null,response:null,activate:function(){var activated=OpenLayers.Strategy.prototype.activate.call(this);if(activated){this.layer.events.on({"moveend":this.update,"refresh":this.update,"visibilitychanged":this.update,scope:this});this.update();}
+return activated;},deactivate:function(){var deactivated=OpenLayers.Strategy.prototype.deactivate.call(this);if(deactivated){this.layer.events.un({"moveend":this.update,"refresh":this.update,"visibilitychanged":this.update,scope:this});}
+return deactivated;},update:function(options){var mapBounds=this.getMapBounds();if(mapBounds!==null&&((options&&options.force)||(this.layer.visibility&&this.layer.calculateInRange()&&this.invalidBounds(mapBounds)))){this.calculateBounds(mapBounds);this.resolution=this.layer.map.getResolution();this.triggerRead(options);}},getMapBounds:function(){if(this.layer.map===null){return null;}
+var bounds=this.layer.map.getExtent();if(bounds&&!this.layer.projection.equals(this.layer.map.getProjectionObject())){bounds=bounds.clone().transform(this.layer.map.getProjectionObject(),this.layer.projection);}
+return bounds;},invalidBounds:function(mapBounds){if(!mapBounds){mapBounds=this.getMapBounds();}
+var invalid=!this.bounds||!this.bounds.containsBounds(mapBounds);if(!invalid&&this.resFactor){var ratio=this.resolution/this.layer.map.getResolution();invalid=(ratio>=this.resFactor||ratio<=(1/this.resFactor));}
+return invalid;},calculateBounds:function(mapBounds){if(!mapBounds){mapBounds=this.getMapBounds();}
+var center=mapBounds.getCenterLonLat();var dataWidth=mapBounds.getWidth()*this.ratio;var dataHeight=mapBounds.getHeight()*this.ratio;this.bounds=new OpenLayers.Bounds(center.lon-(dataWidth/2),center.lat-(dataHeight/2),center.lon+(dataWidth/2),center.lat+(dataHeight/2));},triggerRead:function(options){if(this.response&&!(options&&options.noAbort===true)){this.layer.protocol.abort(this.response);this.layer.events.triggerEvent("loadend");}
+this.layer.events.triggerEvent("loadstart");this.response=this.layer.protocol.read(OpenLayers.Util.applyDefaults({filter:this.createFilter(),callback:this.merge,scope:this},options));},createFilter:function(){var filter=new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.BBOX,value:this.bounds,projection:this.layer.projection});if(this.layer.filter){filter=new OpenLayers.Filter.Logical({type:OpenLayers.Filter.Logical.AND,filters:[this.layer.filter,filter]});}
+return filter;},merge:function(resp){this.layer.destroyFeatures();var features=resp.features;if(features&&features.length>0){var remote=this.layer.projection;var local=this.layer.map.getProjectionObject();if(!local.equals(remote)){var geom;for(var i=0,len=features.length;i<len;++i){geom=features[i].geometry;if(geom){geom.transform(remote,local);}}}
+this.layer.addFeatures(features);}
+this.response=null;this.layer.events.triggerEvent("loadend");},CLASS_NAME:"OpenLayers.Strategy.BBOX"});OpenLayers.Handler.Feature=OpenLayers.Class(OpenLayers.Handler,{EVENTMAP:{'click':{'in':'click','out':'clickout'},'mousemove':{'in':'over','out':'out'},'dblclick':{'in':'dblclick','out':null},'mousedown':{'in':null,'out':null},'mouseup':{'in':null,'out':null},'touchstart':{'in':'click','out':'clickout'}},feature:null,lastFeature:null,down:null,up:null,touch:false,clickTolerance:4,geometryTypes:null,stopClick:true,stopDown:true,stopUp:false,initialize:function(control,layer,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,[control,callbacks,options]);this.layer=layer;},touchstart:function(evt){if(!this.touch){this.touch=true;this.map.events.un({mousedown:this.mousedown,mouseup:this.mouseup,mousemove:this.mousemove,click:this.click,dblclick:this.dblclick,scope:this});}
+return OpenLayers.Event.isMultiTouch(evt)?true:this.mousedown(evt);},touchmove:function(evt){OpenLayers.Event.stop(evt);},mousedown:function(evt){if(OpenLayers.Event.isLeftClick(evt)||OpenLayers.Event.isSingleTouch(evt)){this.down=evt.xy;}
+return this.handle(evt)?!this.stopDown:true;},mouseup:function(evt){this.up=evt.xy;return this.handle(evt)?!this.stopUp:true;},click:function(evt){return this.handle(evt)?!this.stopClick:true;},mousemove:function(evt){if(!this.callbacks['over']&&!this.callbacks['out']){return true;}
+this.handle(evt);return true;},dblclick:function(evt){return!this.handle(evt);},geometryTypeMatches:function(feature){return this.geometryTypes==null||OpenLayers.Util.indexOf(this.geometryTypes,feature.geometry.CLASS_NAME)>-1;},handle:function(evt){if(this.feature&&!this.feature.layer){this.feature=null;}
+var type=evt.type;var handled=false;var previouslyIn=!!(this.feature);var click=(type=="click"||type=="dblclick"||type=="touchstart");this.feature=this.layer.getFeatureFromEvent(evt);if(this.feature&&!this.feature.layer){this.feature=null;}
+if(this.lastFeature&&!this.lastFeature.layer){this.lastFeature=null;}
+if(this.feature){if(type==="touchstart"){OpenLayers.Event.stop(evt);}
+var inNew=(this.feature!=this.lastFeature);if(this.geometryTypeMatches(this.feature)){if(previouslyIn&&inNew){if(this.lastFeature){this.triggerCallback(type,'out',[this.lastFeature]);}
+this.triggerCallback(type,'in',[this.feature]);}else if(!previouslyIn||click){this.triggerCallback(type,'in',[this.feature]);}
+this.lastFeature=this.feature;handled=true;}else{if(this.lastFeature&&(previouslyIn&&inNew||click)){this.triggerCallback(type,'out',[this.lastFeature]);}
+this.feature=null;}}else{if(this.lastFeature&&(previouslyIn||click)){this.triggerCallback(type,'out',[this.lastFeature]);}}
+return handled;},triggerCallback:function(type,mode,args){var key=this.EVENTMAP[type][mode];if(key){if(type=='click'&&this.up&&this.down){var dpx=Math.sqrt(Math.pow(this.up.x-this.down.x,2)+
+Math.pow(this.up.y-this.down.y,2));if(dpx<=this.clickTolerance){this.callback(key,args);}}else{this.callback(key,args);}}},activate:function(){var activated=false;if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.moveLayerToTop();this.map.events.on({"removelayer":this.handleMapEvents,"changelayer":this.handleMapEvents,scope:this});activated=true;}
+return activated;},deactivate:function(){var deactivated=false;if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){this.moveLayerBack();this.feature=null;this.lastFeature=null;this.down=null;this.up=null;this.touch=false;this.map.events.un({"removelayer":this.handleMapEvents,"changelayer":this.handleMapEvents,scope:this});deactivated=true;}
+return deactivated;},handleMapEvents:function(evt){if(evt.type=="removelayer"||evt.property=="order"){this.moveLayerToTop();}},moveLayerToTop:function(){var index=Math.max(this.map.Z_INDEX_BASE['Feature']-1,this.layer.getZIndex())+1;this.layer.setZIndex(index);},moveLayerBack:function(){var index=this.layer.getZIndex()-1;if(index>=this.map.Z_INDEX_BASE['Feature']){this.layer.setZIndex(index);}else{this.map.setLayerZIndex(this.layer,this.map.getLayerIndex(this.layer));}},CLASS_NAME:"OpenLayers.Handler.Feature"});OpenLayers.StyleMap=OpenLayers.Class({styles:null,extendDefault:true,initialize:function(style,options){this.styles={"default":new OpenLayers.Style(OpenLayers.Feature.Vector.style["default"]),"select":new OpenLayers.Style(OpenLayers.Feature.Vector.style["select"]),"temporary":new OpenLayers.Style(OpenLayers.Feature.Vector.style["temporary"]),"delete":new OpenLayers.Style(OpenLayers.Feature.Vector.style["delete"])};if(style instanceof OpenLayers.Style){this.styles["default"]=style;this.styles["select"]=style;this.styles["temporary"]=style;this.styles["delete"]=style;}else if(typeof style=="object"){for(var key in style){if(style[key]instanceof OpenLayers.Style){this.styles[key]=style[key];}else if(typeof style[key]=="object"){this.styles[key]=new OpenLayers.Style(style[key]);}else{this.styles["default"]=new OpenLayers.Style(style);this.styles["select"]=new OpenLayers.Style(style);this.styles["temporary"]=new OpenLayers.Style(style);this.styles["delete"]=new OpenLayers.Style(style);break;}}}
 OpenLayers.Util.extend(this,options);},destroy:function(){for(var key in this.styles){this.styles[key].destroy();}
 this.styles=null;},createSymbolizer:function(feature,intent){if(!feature){feature=new OpenLayers.Feature.Vector();}
 if(!this.styles[intent]){intent="default";}
@@ -1138,9 +1191,55 @@ if(this.imageDiv!=null){if(this.px==null){this.display(false);}else{if(this.calc
 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,{x:this.px.x+this.offset.x,y:this.px.y+this.offset.y});}}},display:function(display){this.imageDiv.style.display=(display)?"":"none";},isDrawn:function(){var isDrawn=(this.imageDiv&&this.imageDiv.parentNode&&(this.imageDiv.parentNode.nodeType!=11));return isDrawn;},CLASS_NAME:"OpenLayers.Icon"});OpenLayers.Marker=OpenLayers.Class({icon:null,lonlat:null,events:null,map:null,initialize:function(lonlat,icon){this.lonlat=lonlat;var newIcon=(icon)?icon:OpenLayers.Marker.defaultIcon();if(this.icon==null){this.icon=newIcon;}else{this.icon.url=newIcon.url;this.icon.size=newIcon.size;this.icon.offset=newIcon.offset;this.icon.calculateOffset=newIcon.calculateOffset;}
 this.events=new OpenLayers.Events(this,this.icon.imageDiv);},destroy:function(){this.erase();this.map=null;this.events.destroy();this.events=null;if(this.icon!=null){this.icon.destroy();this.icon=null;}},draw:function(px){return this.icon.draw(px);},erase:function(){if(this.icon!=null){this.icon.erase();}},moveTo:function(px){if((px!=null)&&(this.icon!=null)){this.icon.moveTo(px);}
 this.lonlat=this.map.getLonLatFromLayerPx(px);},isDrawn:function(){var isDrawn=(this.icon&&this.icon.isDrawn());return isDrawn;},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsLonLat(this.lonlat);}
-return onScreen;},inflate:function(inflate){if(this.icon){this.icon.setSize({w:this.icon.size.w*inflate,h:this.icon.size.h*inflate});}},setOpacity:function(opacity){this.icon.setOpacity(opacity);},setUrl:function(url){this.icon.setUrl(url);},display:function(display){this.icon.display(display);},CLASS_NAME:"OpenLayers.Marker"});OpenLayers.Marker.defaultIcon=function(){return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),{w:21,h:25},{x:-10.5,y:-25});};OpenLayers.Lang["ru"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Необработанный запрос вернул ${statusText}",'Permalink':"Постоянная ссылка",'Overlays':"Слои",'Base Layer':"Основной слой",'noFID':"Невозможно обновить объект, для которого нет FID.",'browserNotSupported':"Ваш браузер не поддерживает векторную графику. На данный момент поддерживаются:\n${renderers}",'minZoomLevelError':"Свойство minZoomLevel предназначено только для использования со слоями, являющимися потомками FixedZoomLevels. То, что этот WFS-слой проверяется на minZoomLevel — реликт прошлого. Однако мы не можем удалить эту функцию, так как, возможно, от неё зависят некоторые основанные на OpenLayers приложения. Функция объявлена устаревшей — проверка minZoomLevel будет удалена в 3.0. Пожалуйста, используйте вместо неё настройку мин/макс разрешения, описанную здесь: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Транзакция WFS: УСПЕШНО ${response}",'commitFailed':"Транзакция WFS: ОШИБКА ${response}",'googleWarning':"Слой Google не удалось нормально загрузить.\x3cbr\x3e\x3cbr\x3eЧтобы избавиться от этого сообщения, выбите другой основной слой в переключателе в правом верхнем углу.\x3cbr\x3e\x3cbr\x3eСкорее всего, причина в том, что библиотека Google Maps не была включена или не содержит корректного API-ключа для вашего сайта.\x3cbr\x3e\x3cbr\x3eРазработчикам: чтобы узнать, как сделать, чтобы всё заработало, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eщёлкните тут\x3c/a\x3e",'getLayerWarning':"Слой ${layerType} не удалось нормально загрузить. \x3cbr\x3e\x3cbr\x3eЧтобы избавиться от этого сообщения, выбите другой основной слой в переключателе в правом верхнем углу.\x3cbr\x3e\x3cbr\x3eСкорее всего, причина в том, что библиотека ${layerLib} не была включена или была включена некорректно.\x3cbr\x3e\x3cbr\x3eРазработчикам: чтобы узнать, как сделать, чтобы всё заработало, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eщёлкните тут\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Масштаб = 1 : ${scaleDenom}",'W':"З",'E':"В",'N':"С",'S':"Ю",'reprojectDeprecated':"Вы используете опцию \'reproject\' для слоя ${layerName}. Эта опция является устаревшей: ее использование предполагалось для поддержки показа данных поверх коммерческих базовых карт, но теперь этот функционал несёт встроенная поддержка сферической проекции Меркатора. Больше сведений доступно на http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Этот метод считается устаревшим и будет удалён в версии 3.0. Пожалуйста, пользуйтесь ${newMethod}."});OpenLayers.Lang["nn"]=OpenLayers.Util.applyDefaults({'Scale = 1 : ${scaleDenom}':"Skala = 1 : ${scaleDenom}"});OpenLayers.Lang["fi"]=OpenLayers.Util.applyDefaults({'Permalink':"Ikilinkki",'Overlays':"Kerrokset",'Base Layer':"Peruskerros",'W':"L",'E':"I",'N':"P",'S':"E"});OpenLayers.Lang.es={'unhandledRequest':"Respuesta a petición no gestionada ${statusText}",'Permalink':"Enlace permanente",'Overlays':"Capas superpuestas",'Base Layer':"Capa Base",'noFID':"No se puede actualizar un elemento para el que no existe FID.",'browserNotSupported':"Su navegador no soporta renderización vectorial. Los renderizadores soportados actualmente son:\n${renderers}",'minZoomLevelError':"La propiedad minZoomLevel debe sólo utilizarse "+"con las capas que tienen FixedZoomLevels. El hecho de que "+"una capa wfs compruebe minZoomLevel es una reliquia del "+"pasado. Sin embargo, no podemos eliminarla sin discontinuar "+"probablemente las aplicaciones OL que puedan depender de ello. "+"Así pues estamos haciéndolo obsoleto --la comprobación "+"minZoomLevel se eliminará en la versión 3.0. Utilice el ajuste "+"de resolution min/max en su lugar, tal como se describe aquí: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transacción WFS: ÉXITO ${response}",'commitFailed':"Transacción WFS: FALLÓ ${response}",'googleWarning':"La capa Google no pudo ser cargada correctamente.<br><br>"+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.<br><br>"+"Probablemente, esto se debe a que el script de la biblioteca de "+"Google Maps no fue correctamente incluido en su página, o no "+"contiene la clave del API correcta para su sitio.<br><br>"+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>haga clic aquí</a>",'getLayerWarning':"La capa ${layerType} no pudo ser cargada correctamente.<br><br>"+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.<br><br>"+"Probablemente, esto se debe a que el script de "+"la biblioteca ${layerLib} "+"no fue correctamente incluido en su página.<br><br>"+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>haga clic aquí</a>",'Scale = 1 : ${scaleDenom}':"Escala = 1 : ${scaleDenom}",'W':'O','E':'E','N':'N','S':'S','Graticule':'Retícula','reprojectDeprecated':"Está usando la opción 'reproject' en la capa "+"${layerName}. Esta opción es obsoleta: su uso fue diseñado "+"para soportar la visualización de datos sobre mapas base comerciales, "+"pero ahora esa funcionalidad debería conseguirse mediante el soporte "+"de la proyección Spherical Mercator. Más información disponible en "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Este método es obsoleto y se eliminará en la versión 3.0. "+"Por favor utilice el método ${newMethod} en su lugar.",'end':''};OpenLayers.Layer.SphericalMercator={getExtent:function(){var extent=null;if(this.sphericalMercator){extent=this.map.calculateBounds();}else{extent=OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);}
+return onScreen;},inflate:function(inflate){if(this.icon){this.icon.setSize({w:this.icon.size.w*inflate,h:this.icon.size.h*inflate});}},setOpacity:function(opacity){this.icon.setOpacity(opacity);},setUrl:function(url){this.icon.setUrl(url);},display:function(display){this.icon.display(display);},CLASS_NAME:"OpenLayers.Marker"});OpenLayers.Marker.defaultIcon=function(){return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),{w:21,h:25},{x:-10.5,y:-25});};OpenLayers.Format.GeoJSON=OpenLayers.Class(OpenLayers.Format.JSON,{ignoreExtraDims:false,read:function(json,type,filter){type=(type)?type:"FeatureCollection";var results=null;var obj=null;if(typeof json=="string"){obj=OpenLayers.Format.JSON.prototype.read.apply(this,[json,filter]);}else{obj=json;}
+if(!obj){OpenLayers.Console.error("Bad JSON: "+json);}else if(typeof(obj.type)!="string"){OpenLayers.Console.error("Bad GeoJSON - no type: "+json);}else if(this.isValidType(obj,type)){switch(type){case"Geometry":try{results=this.parseGeometry(obj);}catch(err){OpenLayers.Console.error(err);}
+break;case"Feature":try{results=this.parseFeature(obj);results.type="Feature";}catch(err){OpenLayers.Console.error(err);}
+break;case"FeatureCollection":results=[];switch(obj.type){case"Feature":try{results.push(this.parseFeature(obj));}catch(err){results=null;OpenLayers.Console.error(err);}
+break;case"FeatureCollection":for(var i=0,len=obj.features.length;i<len;++i){try{results.push(this.parseFeature(obj.features[i]));}catch(err){results=null;OpenLayers.Console.error(err);}}
+break;default:try{var geom=this.parseGeometry(obj);results.push(new OpenLayers.Feature.Vector(geom));}catch(err){results=null;OpenLayers.Console.error(err);}}
+break;}}
+return results;},isValidType:function(obj,type){var valid=false;switch(type){case"Geometry":if(OpenLayers.Util.indexOf(["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon","Box","GeometryCollection"],obj.type)==-1){OpenLayers.Console.error("Unsupported geometry type: "+
+obj.type);}else{valid=true;}
+break;case"FeatureCollection":valid=true;break;default:if(obj.type==type){valid=true;}else{OpenLayers.Console.error("Cannot convert types from "+
+obj.type+" to "+type);}}
+return valid;},parseFeature:function(obj){var feature,geometry,attributes,bbox;attributes=(obj.properties)?obj.properties:{};bbox=(obj.geometry&&obj.geometry.bbox)||obj.bbox;try{geometry=this.parseGeometry(obj.geometry);}catch(err){throw err;}
+feature=new OpenLayers.Feature.Vector(geometry,attributes);if(bbox){feature.bounds=OpenLayers.Bounds.fromArray(bbox);}
+if(obj.id){feature.fid=obj.id;}
+return feature;},parseGeometry:function(obj){if(obj==null){return null;}
+var geometry,collection=false;if(obj.type=="GeometryCollection"){if(!(OpenLayers.Util.isArray(obj.geometries))){throw"GeometryCollection must have geometries array: "+obj;}
+var numGeom=obj.geometries.length;var components=new Array(numGeom);for(var i=0;i<numGeom;++i){components[i]=this.parseGeometry.apply(this,[obj.geometries[i]]);}
+geometry=new OpenLayers.Geometry.Collection(components);collection=true;}else{if(!(OpenLayers.Util.isArray(obj.coordinates))){throw"Geometry must have coordinates array: "+obj;}
+if(!this.parseCoords[obj.type.toLowerCase()]){throw"Unsupported geometry type: "+obj.type;}
+try{geometry=this.parseCoords[obj.type.toLowerCase()].apply(this,[obj.coordinates]);}catch(err){throw err;}}
+if(this.internalProjection&&this.externalProjection&&!collection){geometry.transform(this.externalProjection,this.internalProjection);}
+return geometry;},parseCoords:{"point":function(array){if(this.ignoreExtraDims==false&&array.length!=2){throw"Only 2D points are supported: "+array;}
+return new OpenLayers.Geometry.Point(array[0],array[1]);},"multipoint":function(array){var points=[];var p=null;for(var i=0,len=array.length;i<len;++i){try{p=this.parseCoords["point"].apply(this,[array[i]]);}catch(err){throw err;}
+points.push(p);}
+return new OpenLayers.Geometry.MultiPoint(points);},"linestring":function(array){var points=[];var p=null;for(var i=0,len=array.length;i<len;++i){try{p=this.parseCoords["point"].apply(this,[array[i]]);}catch(err){throw err;}
+points.push(p);}
+return new OpenLayers.Geometry.LineString(points);},"multilinestring":function(array){var lines=[];var l=null;for(var i=0,len=array.length;i<len;++i){try{l=this.parseCoords["linestring"].apply(this,[array[i]]);}catch(err){throw err;}
+lines.push(l);}
+return new OpenLayers.Geometry.MultiLineString(lines);},"polygon":function(array){var rings=[];var r,l;for(var i=0,len=array.length;i<len;++i){try{l=this.parseCoords["linestring"].apply(this,[array[i]]);}catch(err){throw err;}
+r=new OpenLayers.Geometry.LinearRing(l.components);rings.push(r);}
+return new OpenLayers.Geometry.Polygon(rings);},"multipolygon":function(array){var polys=[];var p=null;for(var i=0,len=array.length;i<len;++i){try{p=this.parseCoords["polygon"].apply(this,[array[i]]);}catch(err){throw err;}
+polys.push(p);}
+return new OpenLayers.Geometry.MultiPolygon(polys);},"box":function(array){if(array.length!=2){throw"GeoJSON box coordinates must have 2 elements";}
+return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(array[0][0],array[0][1]),new OpenLayers.Geometry.Point(array[1][0],array[0][1]),new OpenLayers.Geometry.Point(array[1][0],array[1][1]),new OpenLayers.Geometry.Point(array[0][0],array[1][1]),new OpenLayers.Geometry.Point(array[0][0],array[0][1])])]);}},write:function(obj,pretty){var geojson={"type":null};if(OpenLayers.Util.isArray(obj)){geojson.type="FeatureCollection";var numFeatures=obj.length;geojson.features=new Array(numFeatures);for(var i=0;i<numFeatures;++i){var element=obj[i];if(!element instanceof OpenLayers.Feature.Vector){var msg="FeatureCollection only supports collections "+"of features: "+element;throw msg;}
+geojson.features[i]=this.extract.feature.apply(this,[element]);}}else if(obj.CLASS_NAME.indexOf("OpenLayers.Geometry")==0){geojson=this.extract.geometry.apply(this,[obj]);}else if(obj instanceof OpenLayers.Feature.Vector){geojson=this.extract.feature.apply(this,[obj]);if(obj.layer&&obj.layer.projection){geojson.crs=this.createCRSObject(obj);}}
+return OpenLayers.Format.JSON.prototype.write.apply(this,[geojson,pretty]);},createCRSObject:function(object){var proj=object.layer.projection.toString();var crs={};if(proj.match(/epsg:/i)){var code=parseInt(proj.substring(proj.indexOf(":")+1));if(code==4326){crs={"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}};}else{crs={"type":"name","properties":{"name":"EPSG:"+code}};}}
+return crs;},extract:{'feature':function(feature){var geom=this.extract.geometry.apply(this,[feature.geometry]);var json={"type":"Feature","properties":feature.attributes,"geometry":geom};if(feature.fid!=null){json.id=feature.fid;}
+return json;},'geometry':function(geometry){if(geometry==null){return null;}
+if(this.internalProjection&&this.externalProjection){geometry=geometry.clone();geometry.transform(this.internalProjection,this.externalProjection);}
+var geometryType=geometry.CLASS_NAME.split('.')[2];var data=this.extract[geometryType.toLowerCase()].apply(this,[geometry]);var json;if(geometryType=="Collection"){json={"type":"GeometryCollection","geometries":data};}else{json={"type":geometryType,"coordinates":data};}
+return json;},'point':function(point){return[point.x,point.y];},'multipoint':function(multipoint){var array=[];for(var i=0,len=multipoint.components.length;i<len;++i){array.push(this.extract.point.apply(this,[multipoint.components[i]]));}
+return array;},'linestring':function(linestring){var array=[];for(var i=0,len=linestring.components.length;i<len;++i){array.push(this.extract.point.apply(this,[linestring.components[i]]));}
+return array;},'multilinestring':function(multilinestring){var array=[];for(var i=0,len=multilinestring.components.length;i<len;++i){array.push(this.extract.linestring.apply(this,[multilinestring.components[i]]));}
+return array;},'polygon':function(polygon){var array=[];for(var i=0,len=polygon.components.length;i<len;++i){array.push(this.extract.linestring.apply(this,[polygon.components[i]]));}
+return array;},'multipolygon':function(multipolygon){var array=[];for(var i=0,len=multipolygon.components.length;i<len;++i){array.push(this.extract.polygon.apply(this,[multipolygon.components[i]]));}
+return array;},'collection':function(collection){var len=collection.components.length;var array=new Array(len);for(var i=0;i<len;++i){array[i]=this.extract.geometry.apply(this,[collection.components[i]]);}
+return array;}},CLASS_NAME:"OpenLayers.Format.GeoJSON"});OpenLayers.Lang["nn"]=OpenLayers.Util.applyDefaults({'Scale = 1 : ${scaleDenom}':"Skala = 1 : ${scaleDenom}"});OpenLayers.Lang["fi"]=OpenLayers.Util.applyDefaults({'Permalink':"Ikilinkki",'Overlays':"Kerrokset",'Base Layer':"Peruskerros",'W':"L",'E':"I",'N':"P",'S':"E"});OpenLayers.Lang.es={'unhandledRequest':"Respuesta a petición no gestionada ${statusText}",'Permalink':"Enlace permanente",'Overlays':"Capas superpuestas",'Base Layer':"Capa Base",'noFID':"No se puede actualizar un elemento para el que no existe FID.",'browserNotSupported':"Su navegador no soporta renderización vectorial. Los renderizadores soportados actualmente son:\n${renderers}",'minZoomLevelError':"La propiedad minZoomLevel debe sólo utilizarse "+"con las capas que tienen FixedZoomLevels. El hecho de que "+"una capa wfs compruebe minZoomLevel es una reliquia del "+"pasado. Sin embargo, no podemos eliminarla sin discontinuar "+"probablemente las aplicaciones OL que puedan depender de ello. "+"Así pues estamos haciéndolo obsoleto --la comprobación "+"minZoomLevel se eliminará en la versión 3.0. Utilice el ajuste "+"de resolution min/max en su lugar, tal como se describe aquí: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transacción WFS: ÉXITO ${response}",'commitFailed':"Transacción WFS: FALLÓ ${response}",'googleWarning':"La capa Google no pudo ser cargada correctamente.<br><br>"+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.<br><br>"+"Probablemente, esto se debe a que el script de la biblioteca de "+"Google Maps no fue correctamente incluido en su página, o no "+"contiene la clave del API correcta para su sitio.<br><br>"+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>haga clic aquí</a>",'getLayerWarning':"La capa ${layerType} no pudo ser cargada correctamente.<br><br>"+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.<br><br>"+"Probablemente, esto se debe a que el script de "+"la biblioteca ${layerLib} "+"no fue correctamente incluido en su página.<br><br>"+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>haga clic aquí</a>",'Scale = 1 : ${scaleDenom}':"Escala = 1 : ${scaleDenom}",'W':'O','E':'E','N':'N','S':'S','Graticule':'Retícula','reprojectDeprecated':"Está usando la opción 'reproject' en la capa "+"${layerName}. Esta opción es obsoleta: su uso fue diseñado "+"para soportar la visualización de datos sobre mapas base comerciales, "+"pero ahora esa funcionalidad debería conseguirse mediante el soporte "+"de la proyección Spherical Mercator. Más información disponible en "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Este método es obsoleto y se eliminará en la versión 3.0. "+"Por favor utilice el método ${newMethod} en su lugar.",'end':''};OpenLayers.Layer.SphericalMercator={getExtent:function(){var extent=null;if(this.sphericalMercator){extent=this.map.calculateBounds();}else{extent=OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);}
 return extent;},getLonLatFromViewPortPx:function(viewPortPx){return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this,arguments);},getViewPortPxFromLonLat:function(lonlat){return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this,arguments);},initMercatorParameters:function(){this.RESOLUTIONS=[];var maxResolution=156543.03390625;for(var zoom=0;zoom<=this.MAX_ZOOM_LEVEL;++zoom){this.RESOLUTIONS[zoom]=maxResolution/Math.pow(2,zoom);}
-this.units="m";this.projection=this.projection||"EPSG:900913";},forwardMercator:(function(){var gg=new OpenLayers.Projection("EPSG:4326");var sm=new OpenLayers.Projection("EPSG:900913");return function(lon,lat){var point=OpenLayers.Projection.transform({x:lon,y:lat},gg,sm);return new OpenLayers.LonLat(point.x,point.y);};})(),inverseMercator:(function(){var gg=new OpenLayers.Projection("EPSG:4326");var sm=new OpenLayers.Projection("EPSG:900913");return function(x,y){var point=OpenLayers.Projection.transform({x:x,y:y},sm,gg);return new OpenLayers.LonLat(point.x,point.y);};})()};OpenLayers.Lang["hsb"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Wotmołwa njewobdźěłaneho naprašowanja ${statusText}",'Permalink':"Trajny wotkaz",'Overlays':"Naworštowanja",'Base Layer':"Zakładna runina",'noFID':"Funkcija, za kotruž FID njeje, njeda so aktualizować.",'browserNotSupported':"Twój wobhladowak wektorowe rysowanje njepodpěruje. Tuchwilu podpěrowane rysowaki su:\n${renderers}",'minZoomLevelError':"Kajkosć minZoomLevel je jenož za wužiwanje z worštami myslena, kotrež wot FixedZoomLevels pochadźeja. Zo tuta woršta wfs za minZoomLevel přepruwuje, je relikt zańdźenosće. Njemóžemy wšak ju wotstronić, bjeztoho zo aplikacije, kotrež na OpenLayers bazěruja a snano tutu kajkosć wužiwaja, hižo njefunguja. Tohodla smy ju jako zestarjenu woznamjenili -- přepruwowanje za minZoomLevel budu so we wersiji 3.0 wotstronjeć. Prošu wužij město toho nastajenje min/max, kaž je tu wopisane: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS-Transakcija: WUSPĚŠNA ${response}",'commitFailed':"WFS-Transakcija: NJEPORADŹENA ${response}",'googleWarning':"Woršta Google njemóžeše so korektnje začitać.\x3cbr\x3e\x3cbr\x3eZo by tutu zdźělenku wotbył, wubjer nowy BaseLayer z wuběra worštow horjeka naprawo.\x3cbr\x3e\x3cbr\x3eNajskerje so to stawa, dokelž skript biblioteki Google Maps pak njebu zapřijaty pak njewobsahuje korektny kluč API za twoje sydło.\x3cbr\x3e\x3cbr\x3eWuwiwarjo: Za pomoc ke korektnemu fungowanju worštow\n\x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3etu kliknyć\x3c/a\x3e",'getLayerWarning':"Woršta ${layerType} njemóžeše so korektnje začitać.\x3cbr\x3e\x3cbr\x3eZo by tutu zdźělenku wotbył, wubjer nowy BaseLayer z wuběra worštow horjeka naprawo.\x3cbr\x3e\x3cbr\x3eNajskerje so to stawa, dokelž skript biblioteki ${layerLib} njebu korektnje zapřijaty.\x3cbr\x3e\x3cbr\x3eWuwiwarjo: Za pomoc ke korektnemu fungowanju worštow\n\x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3etu kliknyć\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Měritko = 1 : ${scaleDenom}",'W':"Z",'E':"W",'N':"S",'S':"J",'reprojectDeprecated':"Wužiwaš opciju \"reproject\" wořšty ${layerName}. Tuta opcija je zestarjena: jeje wužiwanje bě myslene, zo by zwobraznjenje datow nad komercielnymi bazowymi kartami podpěrało, ale funkcionalnosć měła so nětko z pomocu Sperical Mercator docpěć. Dalše informacije steja na http://trac.openlayers.org/wiki/SphericalMercator k dispoziciji.",'methodDeprecated':"Tuta metoda je so njeschwaliła a budźe so w 3.0 wotstronjeć. Prošu wužij ${newMethod} město toho."});OpenLayers.Lang["de"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Unbehandelte Anfragerückmeldung ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Grundkarte",'noFID':"Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",'browserNotSupported':"Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",'minZoomLevelError':"Die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft ist nur für die Verwendung mit \x3ccode\x3eFixedZoomLevels\x3c/code\x3e-untergeordneten Layers vorgesehen. Das dieser \x3ctt\x3ewfs\x3c/tt\x3e-Layer die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft überprüft ist ein Relikt der Vergangenheit. Wir können diese Überprüfung nicht entfernen, ohne das OL basierende Applikationen nicht mehr funktionieren. Daher markieren wir es als veraltet - die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Überprüfung wird in Version 3.0 entfernt werden. Bitte verwenden Sie stattdessen die Min-/Max-Lösung, wie sie unter http://trac.openlayers.org/wiki/SettingZoomLevels beschrieben ist.",'commitSuccess':"WFS-Transaktion: Erfolgreich ${response}",'commitFailed':"WFS-Transaktion: Fehlgeschlagen ${response}",'googleWarning':"Der Google-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der Google-Maps-Bibliothek nicht eingebunden wurde oder keinen gültigen API-Schlüssel für Ihre URL enthält.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden des Google-Layers",'getLayerWarning':"Der ${layerType}-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der \'${layerLib}\'-Bibliothek nicht eingebunden wurde.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden von Layern",'Scale = 1 : ${scaleDenom}':"Maßstab = 1 : ${scaleDenom}",'W':"W",'E':"O",'N':"N",'S':"S",'reprojectDeprecated':"Sie verwenden die „Reproject“-Option des Layers ${layerName}. Diese Option ist veraltet: Sie wurde entwickelt um die Anzeige von Daten auf kommerziellen Basiskarten zu unterstützen, aber diese Funktion sollte jetzt durch Unterstützung der „Spherical Mercator“ erreicht werden. Weitere Informationen sind unter http://trac.openlayers.org/wiki/SphericalMercator verfügbar.",'methodDeprecated':"Die Methode ist veraltet und wird in 3.0 entfernt. Bitte verwende stattdessen ${newMethod}."});OpenLayers.ProxyHost="";OpenLayers.Request={DEFAULT_CONFIG:{method:"GET",url:window.location.href,async:true,user:undefined,password:undefined,params:null,proxy:OpenLayers.ProxyHost,headers:{},data:null,callback:function(){},success:null,failure:null,scope:null},URL_SPLIT_REGEX:/([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,events:new OpenLayers.Events(this),makeSameOrigin:function(url,proxy){var sameOrigin=url.indexOf("http")!==0;var urlParts=!sameOrigin&&url.match(this.URL_SPLIT_REGEX);if(urlParts){var location=window.location;sameOrigin=urlParts[1]==location.protocol&&urlParts[3]==location.hostname;var uPort=urlParts[4],lPort=location.port;if(uPort!=80&&uPort!=""||lPort!="80"&&lPort!=""){sameOrigin=sameOrigin&&uPort==lPort;}}
+this.units="m";this.projection=this.projection||"EPSG:900913";},forwardMercator:(function(){var gg=new OpenLayers.Projection("EPSG:4326");var sm=new OpenLayers.Projection("EPSG:900913");return function(lon,lat){var point=OpenLayers.Projection.transform({x:lon,y:lat},gg,sm);return new OpenLayers.LonLat(point.x,point.y);};})(),inverseMercator:(function(){var gg=new OpenLayers.Projection("EPSG:4326");var sm=new OpenLayers.Projection("EPSG:900913");return function(x,y){var point=OpenLayers.Projection.transform({x:x,y:y},sm,gg);return new OpenLayers.LonLat(point.x,point.y);};})()};OpenLayers.Lang["ru"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Необработанный запрос вернул ${statusText}",'Permalink':"Постоянная ссылка",'Overlays':"Слои",'Base Layer':"Основной слой",'noFID':"Невозможно обновить объект, для которого нет FID.",'browserNotSupported':"Ваш браузер не поддерживает векторную графику. На данный момент поддерживаются:\n${renderers}",'minZoomLevelError':"Свойство minZoomLevel предназначено только для использования со слоями, являющимися потомками FixedZoomLevels. То, что этот WFS-слой проверяется на minZoomLevel — реликт прошлого. Однако мы не можем удалить эту функцию, так как, возможно, от неё зависят некоторые основанные на OpenLayers приложения. Функция объявлена устаревшей — проверка minZoomLevel будет удалена в 3.0. Пожалуйста, используйте вместо неё настройку мин/макс разрешения, описанную здесь: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Транзакция WFS: УСПЕШНО ${response}",'commitFailed':"Транзакция WFS: ОШИБКА ${response}",'googleWarning':"Слой Google не удалось нормально загрузить.\x3cbr\x3e\x3cbr\x3eЧтобы избавиться от этого сообщения, выбите другой основной слой в переключателе в правом верхнем углу.\x3cbr\x3e\x3cbr\x3eСкорее всего, причина в том, что библиотека Google Maps не была включена или не содержит корректного API-ключа для вашего сайта.\x3cbr\x3e\x3cbr\x3eРазработчикам: чтобы узнать, как сделать, чтобы всё заработало, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eщёлкните тут\x3c/a\x3e",'getLayerWarning':"Слой ${layerType} не удалось нормально загрузить. \x3cbr\x3e\x3cbr\x3eЧтобы избавиться от этого сообщения, выбите другой основной слой в переключателе в правом верхнем углу.\x3cbr\x3e\x3cbr\x3eСкорее всего, причина в том, что библиотека ${layerLib} не была включена или была включена некорректно.\x3cbr\x3e\x3cbr\x3eРазработчикам: чтобы узнать, как сделать, чтобы всё заработало, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eщёлкните тут\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Масштаб = 1 : ${scaleDenom}",'W':"З",'E':"В",'N':"С",'S':"Ю",'reprojectDeprecated':"Вы используете опцию \'reproject\' для слоя ${layerName}. Эта опция является устаревшей: ее использование предполагалось для поддержки показа данных поверх коммерческих базовых карт, но теперь этот функционал несёт встроенная поддержка сферической проекции Меркатора. Больше сведений доступно на http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Этот метод считается устаревшим и будет удалён в версии 3.0. Пожалуйста, пользуйтесь ${newMethod}."});OpenLayers.Lang["hsb"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Wotmołwa njewobdźěłaneho naprašowanja ${statusText}",'Permalink':"Trajny wotkaz",'Overlays':"Naworštowanja",'Base Layer':"Zakładna runina",'noFID':"Funkcija, za kotruž FID njeje, njeda so aktualizować.",'browserNotSupported':"Twój wobhladowak wektorowe rysowanje njepodpěruje. Tuchwilu podpěrowane rysowaki su:\n${renderers}",'minZoomLevelError':"Kajkosć minZoomLevel je jenož za wužiwanje z worštami myslena, kotrež wot FixedZoomLevels pochadźeja. Zo tuta woršta wfs za minZoomLevel přepruwuje, je relikt zańdźenosće. Njemóžemy wšak ju wotstronić, bjeztoho zo aplikacije, kotrež na OpenLayers bazěruja a snano tutu kajkosć wužiwaja, hižo njefunguja. Tohodla smy ju jako zestarjenu woznamjenili -- přepruwowanje za minZoomLevel budu so we wersiji 3.0 wotstronjeć. Prošu wužij město toho nastajenje min/max, kaž je tu wopisane: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS-Transakcija: WUSPĚŠNA ${response}",'commitFailed':"WFS-Transakcija: NJEPORADŹENA ${response}",'googleWarning':"Woršta Google njemóžeše so korektnje začitać.\x3cbr\x3e\x3cbr\x3eZo by tutu zdźělenku wotbył, wubjer nowy BaseLayer z wuběra worštow horjeka naprawo.\x3cbr\x3e\x3cbr\x3eNajskerje so to stawa, dokelž skript biblioteki Google Maps pak njebu zapřijaty pak njewobsahuje korektny kluč API za twoje sydło.\x3cbr\x3e\x3cbr\x3eWuwiwarjo: Za pomoc ke korektnemu fungowanju worštow\n\x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3etu kliknyć\x3c/a\x3e",'getLayerWarning':"Woršta ${layerType} njemóžeše so korektnje začitać.\x3cbr\x3e\x3cbr\x3eZo by tutu zdźělenku wotbył, wubjer nowy BaseLayer z wuběra worštow horjeka naprawo.\x3cbr\x3e\x3cbr\x3eNajskerje so to stawa, dokelž skript biblioteki ${layerLib} njebu korektnje zapřijaty.\x3cbr\x3e\x3cbr\x3eWuwiwarjo: Za pomoc ke korektnemu fungowanju worštow\n\x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3etu kliknyć\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Měritko = 1 : ${scaleDenom}",'W':"Z",'E':"W",'N':"S",'S':"J",'reprojectDeprecated':"Wužiwaš opciju \"reproject\" wořšty ${layerName}. Tuta opcija je zestarjena: jeje wužiwanje bě myslene, zo by zwobraznjenje datow nad komercielnymi bazowymi kartami podpěrało, ale funkcionalnosć měła so nětko z pomocu Sperical Mercator docpěć. Dalše informacije steja na http://trac.openlayers.org/wiki/SphericalMercator k dispoziciji.",'methodDeprecated':"Tuta metoda je so njeschwaliła a budźe so w 3.0 wotstronjeć. Prošu wužij ${newMethod} město toho."});OpenLayers.Lang["de"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Unbehandelte Anfragerückmeldung ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Grundkarte",'noFID':"Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",'browserNotSupported':"Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",'minZoomLevelError':"Die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft ist nur für die Verwendung mit \x3ccode\x3eFixedZoomLevels\x3c/code\x3e-untergeordneten Layers vorgesehen. Das dieser \x3ctt\x3ewfs\x3c/tt\x3e-Layer die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft überprüft ist ein Relikt der Vergangenheit. Wir können diese Überprüfung nicht entfernen, ohne das OL basierende Applikationen nicht mehr funktionieren. Daher markieren wir es als veraltet - die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Überprüfung wird in Version 3.0 entfernt werden. Bitte verwenden Sie stattdessen die Min-/Max-Lösung, wie sie unter http://trac.openlayers.org/wiki/SettingZoomLevels beschrieben ist.",'commitSuccess':"WFS-Transaktion: Erfolgreich ${response}",'commitFailed':"WFS-Transaktion: Fehlgeschlagen ${response}",'googleWarning':"Der Google-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der Google-Maps-Bibliothek nicht eingebunden wurde oder keinen gültigen API-Schlüssel für Ihre URL enthält.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden des Google-Layers",'getLayerWarning':"Der ${layerType}-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der \'${layerLib}\'-Bibliothek nicht eingebunden wurde.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden von Layern",'Scale = 1 : ${scaleDenom}':"Maßstab = 1 : ${scaleDenom}",'W':"W",'E':"O",'N':"N",'S':"S",'reprojectDeprecated':"Sie verwenden die „Reproject“-Option des Layers ${layerName}. Diese Option ist veraltet: Sie wurde entwickelt um die Anzeige von Daten auf kommerziellen Basiskarten zu unterstützen, aber diese Funktion sollte jetzt durch Unterstützung der „Spherical Mercator“ erreicht werden. Weitere Informationen sind unter http://trac.openlayers.org/wiki/SphericalMercator verfügbar.",'methodDeprecated':"Die Methode ist veraltet und wird in 3.0 entfernt. Bitte verwende stattdessen ${newMethod}."});OpenLayers.ProxyHost="";OpenLayers.Request={DEFAULT_CONFIG:{method:"GET",url:window.location.href,async:true,user:undefined,password:undefined,params:null,proxy:OpenLayers.ProxyHost,headers:{},data:null,callback:function(){},success:null,failure:null,scope:null},URL_SPLIT_REGEX:/([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,events:new OpenLayers.Events(this),makeSameOrigin:function(url,proxy){var sameOrigin=url.indexOf("http")!==0;var urlParts=!sameOrigin&&url.match(this.URL_SPLIT_REGEX);if(urlParts){var location=window.location;sameOrigin=urlParts[1]==location.protocol&&urlParts[3]==location.hostname;var uPort=urlParts[4],lPort=location.port;if(uPort!=80&&uPort!=""||lPort!="80"&&lPort!=""){sameOrigin=sameOrigin&&uPort==lPort;}}
 if(!sameOrigin){if(proxy){if(typeof proxy=="function"){url=proxy(url);}else{url=proxy+encodeURIComponent(url);}}else{OpenLayers.Console.warn(OpenLayers.i18n("proxyNeeded"),{url:url});}}
 return url;},issue:function(config){var defaultConfig=OpenLayers.Util.extend(this.DEFAULT_CONFIG,{proxy:OpenLayers.ProxyHost});config=OpenLayers.Util.applyDefaults(config,defaultConfig);var customRequestedWithHeader=false,headerKey;for(headerKey in config.headers){if(config.headers.hasOwnProperty(headerKey)){if(headerKey.toLowerCase()==='x-requested-with'){customRequestedWithHeader=true;}}}
 if(customRequestedWithHeader===false){config.headers['X-Requested-With']='XMLHttpRequest';}
@@ -1206,7 +1305,20 @@ return null;return oDocument;};function fSynchronizeValues(oRequest){try{oReques
 try{oRequest.responseXML=fGetDocument(oRequest._object);}catch(e){}
 try{oRequest.status=oRequest._object.status;}catch(e){}
 try{oRequest.statusText=oRequest._object.statusText;}catch(e){}};function fCleanTransport(oRequest){oRequest._object.onreadystatechange=new window.Function;};if(!window.Function.prototype.apply){window.Function.prototype.apply=function(oRequest,oArguments){if(!oArguments)
-oArguments=[];oRequest.__func=this;oRequest.__func(oArguments[0],oArguments[1],oArguments[2],oArguments[3],oArguments[4]);delete oRequest.__func;};};OpenLayers.Request.XMLHttpRequest=cXMLHttpRequest;})();OpenLayers.Lang["nds"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Unbehannelt Trüchmellels för de Anfraag ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Achtergrundkoort",'noFID':"En Feature, dat keen FID hett, kann nich aktuell maakt warrn.",'browserNotSupported':"Dien Browser ünnerstütt keen Vektorbiller. Ünnerstütt Renderers:\n${renderers}",'commitSuccess':"WFS-Transakschoon: hett klappt ${response}",'commitFailed':"WFS-Transakschoon: hett nich klappt ${response}",'Scale = 1 : ${scaleDenom}':"Skaal = 1 : ${scaleDenom}",'methodDeprecated':"Disse Methood is oold un schall dat in 3.0 nich mehr geven. Bruuk dor man beter ${newMethod} för."});OpenLayers.Renderer.VML=OpenLayers.Class(OpenLayers.Renderer.Elements,{xmlns:"urn:schemas-microsoft-com:vml",symbolCache:{},offset:null,initialize:function(containerID){if(!this.supported()){return;}
+oArguments=[];oRequest.__func=this;oRequest.__func(oArguments[0],oArguments[1],oArguments[2],oArguments[3],oArguments[4]);delete oRequest.__func;};};OpenLayers.Request.XMLHttpRequest=cXMLHttpRequest;})();OpenLayers.Lang["nds"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Unbehannelt Trüchmellels för de Anfraag ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Achtergrundkoort",'noFID':"En Feature, dat keen FID hett, kann nich aktuell maakt warrn.",'browserNotSupported':"Dien Browser ünnerstütt keen Vektorbiller. Ünnerstütt Renderers:\n${renderers}",'commitSuccess':"WFS-Transakschoon: hett klappt ${response}",'commitFailed':"WFS-Transakschoon: hett nich klappt ${response}",'Scale = 1 : ${scaleDenom}':"Skaal = 1 : ${scaleDenom}",'methodDeprecated':"Disse Methood is oold un schall dat in 3.0 nich mehr geven. Bruuk dor man beter ${newMethod} för."});OpenLayers.Handler.RegularPolygon=OpenLayers.Class(OpenLayers.Handler.Drag,{sides:4,radius:null,snapAngle:null,snapToggle:'shiftKey',layerOptions:null,persist:false,irregular:false,citeCompliant:false,angle:null,fixedRadius:false,feature:null,layer:null,origin:null,initialize:function(control,callbacks,options){if(!(options&&options.layerOptions&&options.layerOptions.styleMap)){this.style=OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'],{});}
+OpenLayers.Handler.Drag.prototype.initialize.apply(this,[control,callbacks,options]);this.options=(options)?options:{};},setOptions:function(newOptions){OpenLayers.Util.extend(this.options,newOptions);OpenLayers.Util.extend(this,newOptions);},activate:function(){var activated=false;if(OpenLayers.Handler.Drag.prototype.activate.apply(this,arguments)){var options=OpenLayers.Util.extend({displayInLayerSwitcher:false,calculateInRange:OpenLayers.Function.True,wrapDateLine:this.citeCompliant},this.layerOptions);this.layer=new OpenLayers.Layer.Vector(this.CLASS_NAME,options);this.map.addLayer(this.layer);activated=true;}
+return activated;},deactivate:function(){var deactivated=false;if(OpenLayers.Handler.Drag.prototype.deactivate.apply(this,arguments)){if(this.dragging){this.cancel();}
+if(this.layer.map!=null){this.layer.destroy(false);if(this.feature){this.feature.destroy();}}
+this.layer=null;this.feature=null;deactivated=true;}
+return deactivated;},down:function(evt){this.fixedRadius=!!(this.radius);var maploc=this.layer.getLonLatFromViewPortPx(evt.xy);this.origin=new OpenLayers.Geometry.Point(maploc.lon,maploc.lat);if(!this.fixedRadius||this.irregular){this.radius=this.map.getResolution();}
+if(this.persist){this.clear();}
+this.feature=new OpenLayers.Feature.Vector();this.createGeometry();this.callback("create",[this.origin,this.feature]);this.layer.addFeatures([this.feature],{silent:true});this.layer.drawFeature(this.feature,this.style);},move:function(evt){var maploc=this.layer.getLonLatFromViewPortPx(evt.xy);var point=new OpenLayers.Geometry.Point(maploc.lon,maploc.lat);if(this.irregular){var ry=Math.sqrt(2)*Math.abs(point.y-this.origin.y)/2;this.radius=Math.max(this.map.getResolution()/2,ry);}else if(this.fixedRadius){this.origin=point;}else{this.calculateAngle(point,evt);this.radius=Math.max(this.map.getResolution()/2,point.distanceTo(this.origin));}
+this.modifyGeometry();if(this.irregular){var dx=point.x-this.origin.x;var dy=point.y-this.origin.y;var ratio;if(dy==0){ratio=dx/(this.radius*Math.sqrt(2));}else{ratio=dx/dy;}
+this.feature.geometry.resize(1,this.origin,ratio);this.feature.geometry.move(dx/2,dy/2);}
+this.layer.drawFeature(this.feature,this.style);},up:function(evt){this.finalize();if(this.start==this.last){this.callback("done",[evt.xy]);}},out:function(evt){this.finalize();},createGeometry:function(){this.angle=Math.PI*((1/this.sides)-(1/2));if(this.snapAngle){this.angle+=this.snapAngle*(Math.PI/180);}
+this.feature.geometry=OpenLayers.Geometry.Polygon.createRegularPolygon(this.origin,this.radius,this.sides,this.snapAngle);},modifyGeometry:function(){var angle,point;var ring=this.feature.geometry.components[0];if(ring.components.length!=(this.sides+1)){this.createGeometry();ring=this.feature.geometry.components[0];}
+for(var i=0;i<this.sides;++i){point=ring.components[i];angle=this.angle+(i*2*Math.PI/this.sides);point.x=this.origin.x+(this.radius*Math.cos(angle));point.y=this.origin.y+(this.radius*Math.sin(angle));point.clearBounds();}},calculateAngle:function(point,evt){var alpha=Math.atan2(point.y-this.origin.y,point.x-this.origin.x);if(this.snapAngle&&(this.snapToggle&&!evt[this.snapToggle])){var snapAngleRad=(Math.PI/180)*this.snapAngle;this.angle=Math.round(alpha/snapAngleRad)*snapAngleRad;}else{this.angle=alpha;}},cancel:function(){this.callback("cancel",null);this.finalize();},finalize:function(){this.origin=null;this.radius=this.options.radius;},clear:function(){if(this.layer){this.layer.renderer.clear();this.layer.destroyFeatures();}},callback:function(name,args){if(this.callbacks[name]){this.callbacks[name].apply(this.control,[this.feature.geometry.clone()]);}
+if(!this.persist&&(name=="done"||name=="cancel")){this.clear();}},CLASS_NAME:"OpenLayers.Handler.RegularPolygon"});OpenLayers.Renderer.VML=OpenLayers.Class(OpenLayers.Renderer.Elements,{xmlns:"urn:schemas-microsoft-com:vml",symbolCache:{},offset:null,initialize:function(containerID){if(!this.supported()){return;}
 if(!document.namespaces.olv){document.namespaces.add("olv",this.xmlns);var style=document.createStyleSheet();var shapes=['shape','rect','oval','fill','stroke','imagedata','group','textbox'];for(var i=0,len=shapes.length;i<len;i++){style.addRule('olv\\:'+shapes[i],"behavior: url(#default#VML); "+"position: absolute; display: inline-block;");}}
 OpenLayers.Renderer.Elements.prototype.initialize.apply(this,arguments);},supported:function(){return!!(document.namespaces);},setExtent:function(extent,resolutionChanged){var coordSysUnchanged=OpenLayers.Renderer.Elements.prototype.setExtent.apply(this,arguments);var resolution=this.getResolution();var left=(extent.left/resolution)|0;var top=(extent.top/resolution-this.size.h)|0;if(resolutionChanged||!this.offset){this.offset={x:left,y:top};left=0;top=0;}else{left=left-this.offset.x;top=top-this.offset.y;}
 var org=(left-this.xOffset)+" "+top;this.root.coordorigin=org;var roots=[this.root,this.vectorRoot,this.textRoot];var root;for(var i=0,len=roots.length;i<len;++i){root=roots[i];var size=this.size.w+" "+this.size.h;root.coordsize=size;}
@@ -1270,20 +1382,7 @@ function callback(response){this.callUserCallback(response,options);success=succ
 var queue=types[OpenLayers.State.INSERT];if(queue.length>0){resp.push(this.create(queue,OpenLayers.Util.applyDefaults({callback:insertCallback,scope:this},options.create)));}
 queue=types[OpenLayers.State.UPDATE];for(var i=queue.length-1;i>=0;--i){resp.push(this.update(queue[i],OpenLayers.Util.applyDefaults({callback:callback,scope:this},options.update)));}
 queue=types[OpenLayers.State.DELETE];for(var i=queue.length-1;i>=0;--i){resp.push(this["delete"](queue[i],OpenLayers.Util.applyDefaults({callback:callback,scope:this},options["delete"])));}
-return resp;},abort:function(response){if(response){response.priv.abort();}},callUserCallback:function(resp,options){var opt=options[resp.requestType];if(opt&&opt.callback){opt.callback.call(opt.scope,resp);}},CLASS_NAME:"OpenLayers.Protocol.HTTP"});OpenLayers.Handler.RegularPolygon=OpenLayers.Class(OpenLayers.Handler.Drag,{sides:4,radius:null,snapAngle:null,snapToggle:'shiftKey',layerOptions:null,persist:false,irregular:false,citeCompliant:false,angle:null,fixedRadius:false,feature:null,layer:null,origin:null,initialize:function(control,callbacks,options){if(!(options&&options.layerOptions&&options.layerOptions.styleMap)){this.style=OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'],{});}
-OpenLayers.Handler.Drag.prototype.initialize.apply(this,[control,callbacks,options]);this.options=(options)?options:{};},setOptions:function(newOptions){OpenLayers.Util.extend(this.options,newOptions);OpenLayers.Util.extend(this,newOptions);},activate:function(){var activated=false;if(OpenLayers.Handler.Drag.prototype.activate.apply(this,arguments)){var options=OpenLayers.Util.extend({displayInLayerSwitcher:false,calculateInRange:OpenLayers.Function.True,wrapDateLine:this.citeCompliant},this.layerOptions);this.layer=new OpenLayers.Layer.Vector(this.CLASS_NAME,options);this.map.addLayer(this.layer);activated=true;}
-return activated;},deactivate:function(){var deactivated=false;if(OpenLayers.Handler.Drag.prototype.deactivate.apply(this,arguments)){if(this.dragging){this.cancel();}
-if(this.layer.map!=null){this.layer.destroy(false);if(this.feature){this.feature.destroy();}}
-this.layer=null;this.feature=null;deactivated=true;}
-return deactivated;},down:function(evt){this.fixedRadius=!!(this.radius);var maploc=this.layer.getLonLatFromViewPortPx(evt.xy);this.origin=new OpenLayers.Geometry.Point(maploc.lon,maploc.lat);if(!this.fixedRadius||this.irregular){this.radius=this.map.getResolution();}
-if(this.persist){this.clear();}
-this.feature=new OpenLayers.Feature.Vector();this.createGeometry();this.callback("create",[this.origin,this.feature]);this.layer.addFeatures([this.feature],{silent:true});this.layer.drawFeature(this.feature,this.style);},move:function(evt){var maploc=this.layer.getLonLatFromViewPortPx(evt.xy);var point=new OpenLayers.Geometry.Point(maploc.lon,maploc.lat);if(this.irregular){var ry=Math.sqrt(2)*Math.abs(point.y-this.origin.y)/2;this.radius=Math.max(this.map.getResolution()/2,ry);}else if(this.fixedRadius){this.origin=point;}else{this.calculateAngle(point,evt);this.radius=Math.max(this.map.getResolution()/2,point.distanceTo(this.origin));}
-this.modifyGeometry();if(this.irregular){var dx=point.x-this.origin.x;var dy=point.y-this.origin.y;var ratio;if(dy==0){ratio=dx/(this.radius*Math.sqrt(2));}else{ratio=dx/dy;}
-this.feature.geometry.resize(1,this.origin,ratio);this.feature.geometry.move(dx/2,dy/2);}
-this.layer.drawFeature(this.feature,this.style);},up:function(evt){this.finalize();if(this.start==this.last){this.callback("done",[evt.xy]);}},out:function(evt){this.finalize();},createGeometry:function(){this.angle=Math.PI*((1/this.sides)-(1/2));if(this.snapAngle){this.angle+=this.snapAngle*(Math.PI/180);}
-this.feature.geometry=OpenLayers.Geometry.Polygon.createRegularPolygon(this.origin,this.radius,this.sides,this.snapAngle);},modifyGeometry:function(){var angle,point;var ring=this.feature.geometry.components[0];if(ring.components.length!=(this.sides+1)){this.createGeometry();ring=this.feature.geometry.components[0];}
-for(var i=0;i<this.sides;++i){point=ring.components[i];angle=this.angle+(i*2*Math.PI/this.sides);point.x=this.origin.x+(this.radius*Math.cos(angle));point.y=this.origin.y+(this.radius*Math.sin(angle));point.clearBounds();}},calculateAngle:function(point,evt){var alpha=Math.atan2(point.y-this.origin.y,point.x-this.origin.x);if(this.snapAngle&&(this.snapToggle&&!evt[this.snapToggle])){var snapAngleRad=(Math.PI/180)*this.snapAngle;this.angle=Math.round(alpha/snapAngleRad)*snapAngleRad;}else{this.angle=alpha;}},cancel:function(){this.callback("cancel",null);this.finalize();},finalize:function(){this.origin=null;this.radius=this.options.radius;},clear:function(){if(this.layer){this.layer.renderer.clear();this.layer.destroyFeatures();}},callback:function(name,args){if(this.callbacks[name]){this.callbacks[name].apply(this.control,[this.feature.geometry.clone()]);}
-if(!this.persist&&(name=="done"||name=="cancel")){this.clear();}},CLASS_NAME:"OpenLayers.Handler.RegularPolygon"});OpenLayers.Lang["bg"]=OpenLayers.Util.applyDefaults({'Permalink':"Постоянна препратка",'Base Layer':"Основен слой",'Scale = 1 : ${scaleDenom}':"Мащаб = 1 : ${scaleDenom}",'methodDeprecated':"Този метод е остарял и ще бъде премахват в 3.0. Вместо него използвайте ${newMethod}."});OpenLayers.Lang["hr"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Nepodržani zahtjev ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Osnovna karta",'noFID':"Ne mogu ažurirati značajku za koju ne postoji FID.",'browserNotSupported':"Vaš preglednik ne podržava vektorsko renderiranje. Trenutno podržani rendereri su: ${renderers}",'commitSuccess':"WFS Transakcija: USPJEŠNA ${response}",'commitFailed':"WFS Transakcija: NEUSPJEŠNA ${response}",'Scale = 1 : ${scaleDenom}':"Mjerilo = 1 : ${scaleDenom}",'methodDeprecated':"Ova metoda nije odobrena i biti će maknuta u 3.0. Koristite ${newMethod}."});OpenLayers.Lang["be-tarask"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Неапрацаваны вынік запыту ${statusText}",'Permalink':"Сталая спасылка",'Overlays':"Слаі",'Base Layer':"Базавы слой",'noFID':"Немагчыма абнавіць магчымасьць, для якога не існуе FID.",'browserNotSupported':"Ваш браўзэр не падтрымлівае вэктарную графіку. У цяперашні момант падтрымліваюцца: ${renderers}",'minZoomLevelError':"Уласьцівасьць minZoomLevel прызначана толькі для выкарыстаньня са слаямі вытворнымі ад FixedZoomLevels. Тое, што  гэты wfs-слой правяраецца на minZoomLevel — рэха прошлага. Але мы ня можам выдаліць гэтую магчымасьць, таму што ад яе залежаць некаторыя заснаваныя на OL дастасаваньні. Тым ня менш, праверка minZoomLevel будзе выдаленая ў вэрсіі 3.0. Калі ласка, выкарыстоўваеце замест яе ўстаноўкі мінімальнага/максымальнага памераў, як апісана тут: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS-транзакцыя: ПОСЬПЕХ ${response}",'commitFailed':"WFS-транзакцыя: ПАМЫЛКА ${response}",'googleWarning':"Не атрымалася загрузіць слой Google. \x3cbr\x3e\x3cbr\x3eКаб пазбавіцца гэтага паведамленьня, выберыце новы базавы слой у сьпісе ў верхнім правым куце.\x3cbr\x3e\x3cbr\x3e Хутчэй за ўсё, прычына ў тым, што скрыпт бібліятэкі Google Maps ня быў уключаныя альбо не ўтрымлівае слушны API-ключ для Вашага сайта.\x3cbr\x3e\x3cbr\x3eРаспрацоўшчыкам: Для таго, каб даведацца як зрабіць так, каб усё працавала, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eнацісьніце тут\x3c/a\x3e",'getLayerWarning':"Немагчыма загрузіць слой ${layerType}.\x3cbr\x3e\x3cbr\x3eКаб пазбавіцца гэтага паведамленьня, выберыце новы базавы слой у сьпісе ў верхнім правым куце.\x3cbr\x3e\x3cbr\x3eХутчэй за ўсё, прычына ў тым, што скрыпт бібліятэкі ${layerLib} ня быў слушна ўключаны.\x3cbr\x3e\x3cbr\x3eРаспрацоўшчыкам: Для таго, каб даведацца як зрабіць так, каб усё працавала, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eнацісьніце тут\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Маштаб = 1 : ${scaleDenom}",'W':"З",'E':"У",'N':"Пн",'S':"Пд",'reprojectDeprecated':"Вы выкарыстоўваеце ўстаноўку \'reproject\' для слоя ${layerName}. Гэтая ўстаноўка зьяўляецца састарэлай: яна выкарыстоўвалася для падтрымкі паказу зьвестак на камэрцыйных базавых мапах, але гэта функцыя цяпер рэалізаваная ў убудаванай падтрымцы сфэрычнай праекцыі Мэркатара. Дадатковая інфармацыя ёсьць на http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Гэты мэтад састарэлы і будзе выдалены ў вэрсіі 3.0. Калі ласка, замест яго выкарыстоўвайце ${newMethod}."});OpenLayers.Lang["cs-CZ"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Nezpracovaná návratová hodnota ${statusText}",'Permalink':"Trvalý odkaz",'Overlays':"Překryvné vrstvy",'Base Layer':"Podkladové vrstvy",'noFID':"Nelze aktualizovat prvek, pro který neexistuje FID.",'browserNotSupported':"Váš prohlížeč nepodporuje vykreslování vektorů. Momentálně podporované nástroje jsou::\n${renderers}",'minZoomLevelError':"Vlastnost minZoomLevel by se měla používat pouze s potomky FixedZoomLevels vrstvami. To znamená, že vrstva wfs kontroluje, zda-li minZoomLevel není zbytek z minulosti.Nelze to ovšem vyjmout bez možnosti, že bychom rozbili aplikace postavené na OL, které by na tom mohly záviset. Proto tuto vlastnost nedoporučujeme používat --  kontrola minZoomLevel bude odstraněna ve verzi 3.0. Použijte prosím raději nastavení min/max podle příkaldu popsaného na: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaction: ÚSPĚCH ${response}",'commitFailed':"WFS Transaction: CHYBA ${response}",'googleWarning':"Nepodařilo se správně načíst vrstvu Google.\x3cbr\x3e\x3cbr\x3eAbyste se zbavili této zprávy, zvolte jinou základní vrstvu v přepínači vrstev.\x3cbr\x3e\x3cbr\x3eTo se většinou stává, pokud nebyl načten skript, nebo neobsahuje správný klíč pro API pro tuto stránku.\x3cbr\x3e\x3cbr\x3eVývojáři: Pro pomoc, aby tohle fungovalo , \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eklikněte sem\x3c/a\x3e",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.\x3cbr\x3e\x3cbr\x3eTo get rid of this message, select a new BaseLayer in the layer switcher in the upper-right corner.\x3cbr\x3e\x3cbr\x3eMost likely, this is because the ${layerLib} library script was either not correctly included.\x3cbr\x3e\x3cbr\x3eDevelopers: For help getting this working correctly, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eclick here\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Měřítko = 1 : ${scaleDenom}",'reprojectDeprecated':"Použil jste volbu \'reproject\' ve vrstvě ${layerName}. Tato volba není doporučená: byla zde proto, aby bylo možno zobrazovat data z okomerčních serverů, ale tato funkce je nyní zajištěna pomocí podpory Spherical Mercator. Více informací naleznete na http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Tato metoda je zavržená a bude ve verzi 3.0 odstraněna. Prosím, použijte raději ${newMethod}."});OpenLayers.Lang["br"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Distro evel reked anveret ${statusText}",'Permalink':"Peurliamm",'Overlays':"Gwiskadoù",'Base Layer':"Gwiskad diazez",'noFID':"N\'haller ket hizivaat un elfenn ma n\'eus ket a niverenn-anaout (FID) eviti.",'browserNotSupported':"N\'eo ket skoret an daskor vektorel gant ho merdeer. Setu aze an daskorerioù skoret evit ar poent :\n${renderers}",'minZoomLevelError':"Ne zleer implijout ar perzh minZoomLevel nemet evit gwiskadoù FixedZoomLevels-descendent. Ar fed ma wiria ar gwiskad WHS-se hag-eñ ez eus eus minZoomLevel zo un aspadenn gozh. Koulskoude n\'omp ket evit e ziverkañ kuit da derriñ arloadoù diazezet war OL a c\'hallfe bezañ stag outañ. Setu perak eo dispredet -- Lamet kuit e vo ar gwiriañ minZoomLevel a-is er stumm 3.0. Ober gant an arventennoù bihanañ/brasañ evel deskrivet amañ e plas : http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Treuzgread WFS : MAT EO ${response}",'commitFailed':"Treuzgread WFS Transaction: C\'HWITET ${response}",'googleWarning':"N\'eus ket bet gallet kargañ ar gwiskad Google ent reizh.\x3cbr\x3e\x3cbr\x3eEvit en em zizober eus ar c\'hemenn-mañ, dibabit ur BaseLayer nevez en diuzer gwiskadoù er c\'horn dehoù el laez.\x3cbr\x3e\x3cbr\x3eSur a-walc\'h eo peogwir n\'eo ket bet ensoc\'het levraoueg Google Maps pe neuze ne glot ket an alc\'hwez API gant ho lec\'hienn.\x3cbr\x3e\x3cbr\x3eDiorroerien : Evit reizhañ an dra-se, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eclick here\x3c/a\x3e",'getLayerWarning':"N\'haller ket kargañ ar gwiskad ${layerType} ent reizh.\x3cbr\x3e\x3cbr\x3eEvit en em zizober eus ar c\'hemenn-mañ, dibabit ur BaseLayer nevez en diuzer gwiskadoù er c\'horn dehoù el laez.\x3cbr\x3e\x3cbr\x3eSur a-walc\'h eo peogwir n\'eo ket bet ensoc\'het mat al levraoueg ${layerLib}.\x3cbr\x3e\x3cbr\x3eDiorroerien : Evit gouzout penaos reizhañ an dra-se, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eclick here\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Skeul = 1 : ${scaleDenom}",'W':"K",'E':"R",'N':"N",'S':"S",'reprojectDeprecated':"Emaoc\'h oc\'h implijout an dibarzh \'reproject\' war ar gwiskad ${layerName}. Dispredet eo an dibarzh-mañ : bet eo hag e talveze da ziskwel roadennoù war-c\'horre kartennoù diazez kenwerzhel, un dra hag a c\'haller ober bremañ gant an arc\'hwel dre skor banndres boullek Mercator. Muioc\'h a ditouroù a c\'haller da gaout war http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Dispredet eo an daore-se ha tennet e vo kuit eus ar stumm 3.0. Grit gant ${newMethod} e plas."});OpenLayers.Control.PinchZoom=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,containerCenter:null,pinchOrigin:null,currentCenter:null,autoActivate:true,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.handler=new OpenLayers.Handler.Pinch(this,{start:this.pinchStart,move:this.pinchMove,done:this.pinchDone},this.handlerOptions);},activate:function(){var activated=OpenLayers.Control.prototype.activate.apply(this,arguments);if(activated){this.map.events.on({moveend:this.updateContainerCenter,scope:this});this.updateContainerCenter();}
+return resp;},abort:function(response){if(response){response.priv.abort();}},callUserCallback:function(resp,options){var opt=options[resp.requestType];if(opt&&opt.callback){opt.callback.call(opt.scope,resp);}},CLASS_NAME:"OpenLayers.Protocol.HTTP"});OpenLayers.Lang["bg"]=OpenLayers.Util.applyDefaults({'Permalink':"Постоянна препратка",'Base Layer':"Основен слой",'Scale = 1 : ${scaleDenom}':"Мащаб = 1 : ${scaleDenom}",'methodDeprecated':"Този метод е остарял и ще бъде премахват в 3.0. Вместо него използвайте ${newMethod}."});OpenLayers.Lang["hr"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Nepodržani zahtjev ${statusText}",'Permalink':"Permalink",'Overlays':"Overlays",'Base Layer':"Osnovna karta",'noFID':"Ne mogu ažurirati značajku za koju ne postoji FID.",'browserNotSupported':"Vaš preglednik ne podržava vektorsko renderiranje. Trenutno podržani rendereri su: ${renderers}",'commitSuccess':"WFS Transakcija: USPJEŠNA ${response}",'commitFailed':"WFS Transakcija: NEUSPJEŠNA ${response}",'Scale = 1 : ${scaleDenom}':"Mjerilo = 1 : ${scaleDenom}",'methodDeprecated':"Ova metoda nije odobrena i biti će maknuta u 3.0. Koristite ${newMethod}."});OpenLayers.Lang["be-tarask"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Неапрацаваны вынік запыту ${statusText}",'Permalink':"Сталая спасылка",'Overlays':"Слаі",'Base Layer':"Базавы слой",'noFID':"Немагчыма абнавіць магчымасьць, для якога не існуе FID.",'browserNotSupported':"Ваш браўзэр не падтрымлівае вэктарную графіку. У цяперашні момант падтрымліваюцца: ${renderers}",'minZoomLevelError':"Уласьцівасьць minZoomLevel прызначана толькі для выкарыстаньня са слаямі вытворнымі ад FixedZoomLevels. Тое, што  гэты wfs-слой правяраецца на minZoomLevel — рэха прошлага. Але мы ня можам выдаліць гэтую магчымасьць, таму што ад яе залежаць некаторыя заснаваныя на OL дастасаваньні. Тым ня менш, праверка minZoomLevel будзе выдаленая ў вэрсіі 3.0. Калі ласка, выкарыстоўваеце замест яе ўстаноўкі мінімальнага/максымальнага памераў, як апісана тут: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS-транзакцыя: ПОСЬПЕХ ${response}",'commitFailed':"WFS-транзакцыя: ПАМЫЛКА ${response}",'googleWarning':"Не атрымалася загрузіць слой Google. \x3cbr\x3e\x3cbr\x3eКаб пазбавіцца гэтага паведамленьня, выберыце новы базавы слой у сьпісе ў верхнім правым куце.\x3cbr\x3e\x3cbr\x3e Хутчэй за ўсё, прычына ў тым, што скрыпт бібліятэкі Google Maps ня быў уключаныя альбо не ўтрымлівае слушны API-ключ для Вашага сайта.\x3cbr\x3e\x3cbr\x3eРаспрацоўшчыкам: Для таго, каб даведацца як зрабіць так, каб усё працавала, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eнацісьніце тут\x3c/a\x3e",'getLayerWarning':"Немагчыма загрузіць слой ${layerType}.\x3cbr\x3e\x3cbr\x3eКаб пазбавіцца гэтага паведамленьня, выберыце новы базавы слой у сьпісе ў верхнім правым куце.\x3cbr\x3e\x3cbr\x3eХутчэй за ўсё, прычына ў тым, што скрыпт бібліятэкі ${layerLib} ня быў слушна ўключаны.\x3cbr\x3e\x3cbr\x3eРаспрацоўшчыкам: Для таго, каб даведацца як зрабіць так, каб усё працавала, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eнацісьніце тут\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Маштаб = 1 : ${scaleDenom}",'W':"З",'E':"У",'N':"Пн",'S':"Пд",'reprojectDeprecated':"Вы выкарыстоўваеце ўстаноўку \'reproject\' для слоя ${layerName}. Гэтая ўстаноўка зьяўляецца састарэлай: яна выкарыстоўвалася для падтрымкі паказу зьвестак на камэрцыйных базавых мапах, але гэта функцыя цяпер рэалізаваная ў убудаванай падтрымцы сфэрычнай праекцыі Мэркатара. Дадатковая інфармацыя ёсьць на http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Гэты мэтад састарэлы і будзе выдалены ў вэрсіі 3.0. Калі ласка, замест яго выкарыстоўвайце ${newMethod}."});OpenLayers.Lang["cs-CZ"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Nezpracovaná návratová hodnota ${statusText}",'Permalink':"Trvalý odkaz",'Overlays':"Překryvné vrstvy",'Base Layer':"Podkladové vrstvy",'noFID':"Nelze aktualizovat prvek, pro který neexistuje FID.",'browserNotSupported':"Váš prohlížeč nepodporuje vykreslování vektorů. Momentálně podporované nástroje jsou::\n${renderers}",'minZoomLevelError':"Vlastnost minZoomLevel by se měla používat pouze s potomky FixedZoomLevels vrstvami. To znamená, že vrstva wfs kontroluje, zda-li minZoomLevel není zbytek z minulosti.Nelze to ovšem vyjmout bez možnosti, že bychom rozbili aplikace postavené na OL, které by na tom mohly záviset. Proto tuto vlastnost nedoporučujeme používat --  kontrola minZoomLevel bude odstraněna ve verzi 3.0. Použijte prosím raději nastavení min/max podle příkaldu popsaného na: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaction: ÚSPĚCH ${response}",'commitFailed':"WFS Transaction: CHYBA ${response}",'googleWarning':"Nepodařilo se správně načíst vrstvu Google.\x3cbr\x3e\x3cbr\x3eAbyste se zbavili této zprávy, zvolte jinou základní vrstvu v přepínači vrstev.\x3cbr\x3e\x3cbr\x3eTo se většinou stává, pokud nebyl načten skript, nebo neobsahuje správný klíč pro API pro tuto stránku.\x3cbr\x3e\x3cbr\x3eVývojáři: Pro pomoc, aby tohle fungovalo , \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eklikněte sem\x3c/a\x3e",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.\x3cbr\x3e\x3cbr\x3eTo get rid of this message, select a new BaseLayer in the layer switcher in the upper-right corner.\x3cbr\x3e\x3cbr\x3eMost likely, this is because the ${layerLib} library script was either not correctly included.\x3cbr\x3e\x3cbr\x3eDevelopers: For help getting this working correctly, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eclick here\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Měřítko = 1 : ${scaleDenom}",'reprojectDeprecated':"Použil jste volbu \'reproject\' ve vrstvě ${layerName}. Tato volba není doporučená: byla zde proto, aby bylo možno zobrazovat data z okomerčních serverů, ale tato funkce je nyní zajištěna pomocí podpory Spherical Mercator. Více informací naleznete na http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Tato metoda je zavržená a bude ve verzi 3.0 odstraněna. Prosím, použijte raději ${newMethod}."});OpenLayers.Lang["br"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Distro evel reked anveret ${statusText}",'Permalink':"Peurliamm",'Overlays':"Gwiskadoù",'Base Layer':"Gwiskad diazez",'noFID':"N\'haller ket hizivaat un elfenn ma n\'eus ket a niverenn-anaout (FID) eviti.",'browserNotSupported':"N\'eo ket skoret an daskor vektorel gant ho merdeer. Setu aze an daskorerioù skoret evit ar poent :\n${renderers}",'minZoomLevelError':"Ne zleer implijout ar perzh minZoomLevel nemet evit gwiskadoù FixedZoomLevels-descendent. Ar fed ma wiria ar gwiskad WHS-se hag-eñ ez eus eus minZoomLevel zo un aspadenn gozh. Koulskoude n\'omp ket evit e ziverkañ kuit da derriñ arloadoù diazezet war OL a c\'hallfe bezañ stag outañ. Setu perak eo dispredet -- Lamet kuit e vo ar gwiriañ minZoomLevel a-is er stumm 3.0. Ober gant an arventennoù bihanañ/brasañ evel deskrivet amañ e plas : http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Treuzgread WFS : MAT EO ${response}",'commitFailed':"Treuzgread WFS Transaction: C\'HWITET ${response}",'googleWarning':"N\'eus ket bet gallet kargañ ar gwiskad Google ent reizh.\x3cbr\x3e\x3cbr\x3eEvit en em zizober eus ar c\'hemenn-mañ, dibabit ur BaseLayer nevez en diuzer gwiskadoù er c\'horn dehoù el laez.\x3cbr\x3e\x3cbr\x3eSur a-walc\'h eo peogwir n\'eo ket bet ensoc\'het levraoueg Google Maps pe neuze ne glot ket an alc\'hwez API gant ho lec\'hienn.\x3cbr\x3e\x3cbr\x3eDiorroerien : Evit reizhañ an dra-se, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eclick here\x3c/a\x3e",'getLayerWarning':"N\'haller ket kargañ ar gwiskad ${layerType} ent reizh.\x3cbr\x3e\x3cbr\x3eEvit en em zizober eus ar c\'hemenn-mañ, dibabit ur BaseLayer nevez en diuzer gwiskadoù er c\'horn dehoù el laez.\x3cbr\x3e\x3cbr\x3eSur a-walc\'h eo peogwir n\'eo ket bet ensoc\'het mat al levraoueg ${layerLib}.\x3cbr\x3e\x3cbr\x3eDiorroerien : Evit gouzout penaos reizhañ an dra-se, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eclick here\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Skeul = 1 : ${scaleDenom}",'W':"K",'E':"R",'N':"N",'S':"S",'reprojectDeprecated':"Emaoc\'h oc\'h implijout an dibarzh \'reproject\' war ar gwiskad ${layerName}. Dispredet eo an dibarzh-mañ : bet eo hag e talveze da ziskwel roadennoù war-c\'horre kartennoù diazez kenwerzhel, un dra hag a c\'haller ober bremañ gant an arc\'hwel dre skor banndres boullek Mercator. Muioc\'h a ditouroù a c\'haller da gaout war http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Dispredet eo an daore-se ha tennet e vo kuit eus ar stumm 3.0. Grit gant ${newMethod} e plas."});OpenLayers.Control.PinchZoom=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,containerCenter:null,pinchOrigin:null,currentCenter:null,autoActivate:true,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.handler=new OpenLayers.Handler.Pinch(this,{start:this.pinchStart,move:this.pinchMove,done:this.pinchDone},this.handlerOptions);},activate:function(){var activated=OpenLayers.Control.prototype.activate.apply(this,arguments);if(activated){this.map.events.on({moveend:this.updateContainerCenter,scope:this});this.updateContainerCenter();}
 return activated;},deactivate:function(){var deactivated=OpenLayers.Control.prototype.deactivate.apply(this,arguments);if(this.map&&this.map.events){this.map.events.un({moveend:this.updateContainerCenter,scope:this});}
 return deactivated;},updateContainerCenter:function(){var container=this.map.layerContainerDiv;this.containerCenter={x:parseInt(container.style.left,10)+50,y:parseInt(container.style.top,10)+50};},pinchStart:function(evt,pinchData){this.pinchOrigin=evt.xy;this.currentCenter=evt.xy;},pinchMove:function(evt,pinchData){var scale=pinchData.scale;var containerCenter=this.containerCenter;var pinchOrigin=this.pinchOrigin;var current=evt.xy;var dx=Math.round((current.x-pinchOrigin.x)+(scale-1)*(containerCenter.x-pinchOrigin.x));var dy=Math.round((current.y-pinchOrigin.y)+(scale-1)*(containerCenter.y-pinchOrigin.y));this.applyTransform("translate("+dx+"px, "+dy+"px) scale("+scale+")");this.currentCenter=current;},applyTransform:function(transform){var style=this.map.layerContainerDiv.style;style['-webkit-transform']=transform;style['-moz-transform']=transform;},pinchDone:function(evt,start,last){this.applyTransform("");var zoom=this.map.getZoomForResolution(this.map.getResolution()/last.scale,true);if(zoom!==this.map.getZoom()||!this.currentCenter.equals(this.pinchOrigin)){var resolution=this.map.getResolutionForZoom(zoom);var location=this.map.getLonLatFromPixel(this.pinchOrigin);var zoomPixel=this.currentCenter;var size=this.map.getSize();location.lon+=resolution*((size.w/2)-zoomPixel.x);location.lat-=resolution*((size.h/2)-zoomPixel.y);this.map.div.clientWidth=this.map.div.clientWidth;this.map.setCenter(location,zoom);}},CLASS_NAME:"OpenLayers.Control.PinchZoom"});OpenLayers.Lang["io"]=OpenLayers.Util.applyDefaults({'Scale = 1 : ${scaleDenom}':"Skalo = 1 : ${scaleDenom}"});OpenLayers.Control.LayerSwitcher=OpenLayers.Class(OpenLayers.Control,{roundedCorner:false,roundedCornerColor:"darkblue",layerStates:null,layersDiv:null,baseLayersDiv:null,baseLayers:null,dataLbl:null,dataLayersDiv:null,dataLayers:null,minimizeDiv:null,maximizeDiv:null,ascending:true,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.layerStates=[];if(this.roundedCorner){OpenLayers.Console.warn('roundedCorner option is deprecated');}},destroy:function(){this.clearLayersArray("base");this.clearLayersArray("data");this.map.events.un({buttonclick:this.onButtonClick,addlayer:this.redraw,changelayer:this.redraw,removelayer:this.redraw,changebaselayer:this.redraw,scope:this});this.events.unregister("buttonclick",this,this.onButtonClick);OpenLayers.Control.prototype.destroy.apply(this,arguments);},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);this.map.events.on({addlayer:this.redraw,changelayer:this.redraw,removelayer:this.redraw,changebaselayer:this.redraw,scope:this});if(this.outsideViewport){this.events.attachToElement(this.div);this.events.register("buttonclick",this,this.onButtonClick);}else{this.map.events.register("buttonclick",this,this.onButtonClick);}},draw:function(){OpenLayers.Control.prototype.draw.apply(this);this.loadContents();if(!this.outsideViewport){this.minimizeControl();}
 this.redraw();return this.div;},onButtonClick:function(evt){var button=evt.buttonElement;if(button===this.minimizeDiv){this.minimizeControl();}else if(button===this.maximizeDiv){this.maximizeControl();}else if(button._layerSwitcher===this.id){if(button["for"]){button=document.getElementById(button["for"]);}