From: Tom Hughes Date: Thu, 28 Mar 2013 00:36:07 +0000 (+0000) Subject: Merge branch 'master' into notes X-Git-Tag: live~5616^2~13 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/898cc828dd1f1167f85abbf35c8e3f0ed640ac1e?hp=d53bbe53f48d3f235ebc93af665ac912c057a51b Merge branch 'master' into notes Conflicts: Gemfile.lock --- diff --git a/Gemfile b/Gemfile index 787a08640..f841dc19a 100644 --- a/Gemfile +++ b/Gemfile @@ -29,6 +29,7 @@ gem 'paperclip', '~> 2.0' gem 'deadlock_retry', '>= 1.2.0' gem 'i18n-js', '>= 3.0.0.rc2' gem 'rack-cors' +gem 'jsonify-rails' # We need ruby-openid 2.2.0 or later for ruby 1.9 support gem 'ruby-openid', '>= 2.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index a53bc3d0d..7679a6ccb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -51,7 +51,7 @@ GEM erubis (2.7.0) execjs (1.4.0) multi_json (~> 1.0) - faraday (0.8.6) + faraday (0.8.7) multipart-post (~> 1.1) hike (1.2.1) htmlentities (4.3.1) @@ -67,6 +67,11 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.7.7) + jsonify (0.3.1) + multi_json (~> 1.0) + jsonify-rails (0.3.2) + actionpack + jsonify (< 0.4.0) jwt (0.1.8) multi_json (>= 1.5) libv8 (3.3.10.4) @@ -102,7 +107,7 @@ GEM activesupport (>= 2.3.2) cocaine (>= 0.0.2) mime-types - pg (0.14.1) + pg (0.15.0) polyglot (0.3.3) r2 (0.1.0) rack (1.4.5) @@ -134,7 +139,7 @@ GEM rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) - rake (10.0.3) + rake (10.0.4) rdoc (3.12.2) json (~> 1.4) redcarpet (2.2.2) @@ -154,7 +159,7 @@ GEM tilt (~> 1.1, != 1.3.0) therubyracer (0.10.2) libv8 (~> 3.3.10) - thor (0.17.0) + thor (0.18.0) tilt (1.3.6) timecop (0.6.1) treetop (1.4.12) @@ -183,6 +188,7 @@ DEPENDENCIES i18n-js (>= 3.0.0.rc2) iconv (= 0.1) jquery-rails + jsonify-rails libxml-ruby (>= 2.0.5) memcached (>= 1.4.1) minitest diff --git a/Vendorfile b/Vendorfile index d11ca37fc..197bf575b 100644 --- a/Vendorfile +++ b/Vendorfile @@ -25,4 +25,11 @@ folder 'vendor/assets' do file 'leaflet.osm.js', 'leaflet-osm.js' end end + + folder 'ohauth' do + from 'git://github.com/tmcw/ohauth.git' do + file 'ohauth.js' + file 'sha.js' + end + end end diff --git a/app/assets/images/closed_note_marker.png b/app/assets/images/closed_note_marker.png new file mode 100644 index 000000000..bf6d6bb25 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 index 000000000..671cf424c 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 index 000000000..a58031663 Binary files /dev/null and b/app/assets/images/open_note_marker.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8d6c13503..284ec77e6 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,6 +10,7 @@ //= require leaflet.pan //= require leaflet.zoom //= require i18n/translations +//= require oauth //= require osm //= require piwik //= require map diff --git a/app/assets/javascripts/browse.js b/app/assets/javascripts/browse.js index c3d67e57e..7149b3481 100644 --- a/app/assets/javascripts/browse.js +++ b/app/assets/javascripts/browse.js @@ -43,7 +43,27 @@ $(document).ready(function () { }); var centre = bbox.getCenter(); - updatelinks(centre.lng, centre.lat, 16, null, params.minlon, params.minlat, params.maxlon, params.maxlat); + updatelinks(centre.lon, centre.lat, 16, null, params.minlon, params.minlat, params.maxlon, params.maxlat); + } else if (params.type == "note") { + var object = {type: params.type, id: params.id}; + + map.setView([params.lat, params.lon], 16); + + L.marker([params.lat, params.lon], { icon: getUserIcon() }).addTo(map); + + var bbox = map.getBounds(); + + $("#loading").hide(); + $("#browse_map .geolink").show(); + + $("a[data-editor=remote]").click(function () { + return remoteEditHandler(bbox); + }); + + updatelinks(params.lon, params.lat, 16, null, + bbox.getWestLng(), bbox.getSouthLat(), + bbox.getEastLng(), bbox.getNorthLat(), + object); } else { $("#object_larger_map").hide(); $("#object_edit").hide(); diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index e96ffbf02..7c80b0108 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -2,6 +2,7 @@ //= require index/browse //= require index/export //= require index/key +//= require index/notes $(document).ready(function () { var permalinks = $("#permalink").detach().html(); diff --git a/app/assets/javascripts/index/notes.js.erb b/app/assets/javascripts/index/notes.js.erb new file mode 100644 index 000000000..e67749f07 --- /dev/null +++ b/app/assets/javascripts/index/notes.js.erb @@ -0,0 +1,270 @@ +//= require templates/notes/show +//= require templates/notes/new + +$(document).ready(function () { + var params = OSM.mapParams(); + + var noteIcons = { + "new": L.icon({ + iconUrl: "<%= image_path 'new_note_marker.png' %>", + iconSize: [22, 22], + iconAnchor: [11, 11] + }), + "open": L.icon({ + iconUrl: "<%= image_path 'open_note_marker.png' %>", + iconSize: [22, 22], + iconAnchor: [11, 11] + }), + "closed": L.icon({ + iconUrl: "<%= image_path 'closed_note_marker.png' %>", + iconSize: [22, 22], + iconAnchor: [11, 11] + }) + }; + + var noteLayer = new L.LayerGroup(); + var notes = {}; + var newNote; + + map.on("layeradd", function (e) { + if (e.layer == noteLayer) { + loadNotes(); + map.on("moveend", loadNotes); + } + }); + + map.on("layerremove", function (e) { + if (e.layer == noteLayer) { + map.off("moveend", loadNotes); + noteLayer.clearLayers(); + notes = {}; + } + }); + + map.on("popupclose", function (e) { + if (newNote && e.popup == newNote._popup) { + $(newNote).oneTime(10, "removenote", function () { + map.removeLayer(newNote); + newNote = null; + }); + } + }); + + if (OSM.STATUS != 'api_offline' && OSM.STATUS != 'database_offline') { + map.layersControl.addOverlay(noteLayer, I18n.t("browse.start_rjs.notes_layer_name")); + + if (params.notes) map.addLayer(noteLayer); + + if (params.note) { + $.ajax({ + url: "/api/" + OSM.API_VERSION + "/notes/" + params.note + ".json", + success: function (feature) { + var marker = updateMarker(notes[feature.properties.id], feature); + + notes[feature.properties.id] = marker; + + map.addLayer(noteLayer); + marker.openPopup(); + } + }); + } + } + + function updateMarker(marker, feature) { + var icon = noteIcons[feature.properties.status]; + var popupContent = createPopupContent(marker, feature.properties); + + if (marker) + { + marker.setIcon(noteIcons[feature.properties.status]); + marker._popup.setContent(popupContent); + } + else + { + marker = L.marker(feature.geometry.coordinates.reverse(), { + icon: icon, + opacity: 0.7 + }); + + marker.addTo(noteLayer).bindPopup(popupContent, popupOptions()); + } + + return marker; + } + + var noteLoader; + + function loadNotes() { + var bounds = map.getBounds(); + var size = bounds.getSize(); + + if (size <= OSM.MAX_NOTE_REQUEST_AREA) { + var url = "/api/" + OSM.API_VERSION + "/notes.json?bbox=" + bounds.toBBOX(); + + if (noteLoader) noteLoader.abort(); + + noteLoader = $.ajax({ + url: url, + success: function (json) { + var oldNotes = notes; + + notes = {}; + + json.features.forEach(function (feature) { + var marker = oldNotes[feature.properties.id]; + + delete oldNotes[feature.properties.id]; + + notes[feature.properties.id] = updateMarker(marker, feature); + }); + + for (id in oldNotes) { + noteLayer.removeLayer(oldNotes[id]); + } + + noteLoader = null; + } + }); + } + }; + + function popupOptions() { + var mapSize = map.getSize(); + + return { + minWidth: 320, + maxWidth: mapSize.y * 1 / 3, + maxHeight: mapSize.y * 2 / 3, + offset: new L.Point(0, -3), + autoPanPadding: new L.Point(60, 40) + }; + } + + function createPopupContent(marker, properties) { + var content = $(JST["templates/notes/show"]({ note: properties })); + + content.find("textarea").on("input", function (e) { + var form = e.target.form; + + if ($(e.target).val() == "") { + $(form.close).val(I18n.t("javascripts.notes.show.resolve")); + $(form.comment).prop("disabled", true); + } else { + $(form.close).val(I18n.t("javascripts.notes.show.comment_and_resolve")); + $(form.comment).prop("disabled", false); + } + }); + + content.find("input[type=submit]").on("click", function (e) { + e.preventDefault(); + updateNote(marker, e.target.form, $(e.target).data("url")); + }); + + return content[0]; + } + + function createNote(marker, form, url) { + var location = marker.getLatLng(); + + $(form).find("input[type=submit]").prop("disabled", true); + + $.ajax({ + url: url, + type: "POST", + oauth: true, + data: { + lat: location.lat, + lon: location.lng, + text: $(form.text).val() + }, + success: function (feature) { + notes[feature.properties.id] = updateMarker(marker, feature); + newNote = null; + + $("#createnoteanchor").removeClass("disabled").addClass("geolink"); + } + }); + } + + function updateNote(marker, form, url) { + $(form).find("input[type=submit]").prop("disabled", true); + + $.ajax({ + url: url, + type: "POST", + oauth: true, + data: { + text: $(form.text).val() + }, + success: function (feature) { + var popupContent = createPopupContent(marker, feature.properties); + + marker.setIcon(noteIcons[feature.properties.status]); + marker._popup.setContent(popupContent); + } + }); + } + + $("#createnoteanchor").click(function (e) { + e.preventDefault(); + + if ($(e.target).hasClass("disabled")) return; + + $(e.target).removeClass("geolink").addClass("disabled"); + + map.addLayer(noteLayer); + + var mapSize = map.getSize(); + var markerPosition; + + if (mapSize.y > 800) + { + markerPosition = [mapSize.x / 2, mapSize.y / 2]; + } + else if (mapSize.y > 400) + { + markerPosition = [mapSize.x / 2, 400]; + } + else + { + markerPosition = [mapSize.x / 2, mapSize.y]; + } + + newNote = L.marker(map.containerPointToLatLng(markerPosition), { + icon: noteIcons["new"], + opacity: 0.7, + draggable: true + }); + + var popupContent = $(JST["templates/notes/new"]({ create_url: $(e.target).attr("href") })); + + popupContent.find("textarea").on("input", function (e) { + var form = e.target.form; + + if ($(e.target).val() == "") { + $(form.add).prop("disabled", true); + } else { + $(form.add).prop("disabled", false); + } + }); + + popupContent.find("input[type=submit]").on("click", function (e) { + e.preventDefault(); + createNote(newNote, e.target.form, $(e.target).data("url")); + }); + + newNote.addTo(noteLayer).bindPopup(popupContent[0], popupOptions()).openPopup(); + + newNote.on("remove", function (e) { + $("#createnoteanchor").removeClass("disabled").addClass("geolink"); + }); + + newNote.on("dragstart", function (e) { + $(newNote).stopTime("removenote"); + }); + + newNote.on("dragend", function (e) { + e.target.openPopup(); + }); + }); +}); diff --git a/app/assets/javascripts/oauth.js b/app/assets/javascripts/oauth.js new file mode 100644 index 000000000..73e1761f2 --- /dev/null +++ b/app/assets/javascripts/oauth.js @@ -0,0 +1,32 @@ +//= require sha +//= require ohauth + +$(document).ready(function () { + $.ajaxPrefilter(function(options, jqxhr) { + if (options.oauth) { + var ohauth = window.ohauth; + var url = options.url.replace(/\?$/, ""); + var params = { + oauth_consumer_key: OSM.oauth_consumer_key, + oauth_token: OSM.oauth_token, + oauth_signature_method: "HMAC-SHA1", + oauth_timestamp: ohauth.timestamp(), + oauth_nonce: ohauth.nonce() + }; + + for (var name in jqxhr.data) { + params[name] = jqxhr.data[name]; + } + + params.oauth_signature = ohauth.signature( + OSM.oauth_consumer_secret, + OSM.oauth_token_secret, + ohauth.baseString(options.type, url, params) + ); + + options.headers = { + Authorization: "OAuth " + ohauth.authHeader(params) + }; + } + }); +}); diff --git a/app/assets/javascripts/osm.js.erb b/app/assets/javascripts/osm.js.erb index f3c1c3607..81c1e3152 100644 --- a/app/assets/javascripts/osm.js.erb +++ b/app/assets/javascripts/osm.js.erb @@ -1,13 +1,14 @@ OSM = { <% if defined?(PIWIK_LOCATION) and defined?(PIWIK_SITE) %> - PIWIK_LOCATION: <%= PIWIK_LOCATION.to_json %>, - PIWIK_SITE: <%= PIWIK_SITE.to_json %>, + PIWIK_LOCATION: <%= PIWIK_LOCATION.to_json %>, + PIWIK_SITE: <%= PIWIK_SITE.to_json %>, <% end %> - MAX_REQUEST_AREA: <%= MAX_REQUEST_AREA.to_json %>, - SERVER_URL: <%= SERVER_URL.to_json %>, - API_VERSION: <%= API_VERSION.to_json %>, - STATUS: <%= STATUS.to_json %>, + MAX_REQUEST_AREA: <%= MAX_REQUEST_AREA.to_json %>, + SERVER_URL: <%= SERVER_URL.to_json %>, + API_VERSION: <%= API_VERSION.to_json %>, + STATUS: <%= STATUS.to_json %>, + MAX_NOTE_REQUEST_AREA: <%= MAX_NOTE_REQUEST_AREA.to_json %>, apiUrl: function (object) { var url = "/api/" + OSM.API_VERSION + "/" + object.type + "/" + object.id; @@ -110,6 +111,12 @@ OSM = { mapParams.lat = (mapParams.minlat + mapParams.maxlat) / 2; } + mapParams.notes = params.notes == "yes"; + + if (params.note) { + mapParams.note = parseInt(params.note); + } + var scale = parseFloat(params.scale); if (scale > 0) { mapParams.zoom = Math.log(360.0 / (scale * 512.0)) / Math.log(2.0); diff --git a/app/assets/javascripts/templates/notes/new.jst.ejs b/app/assets/javascripts/templates/notes/new.jst.ejs new file mode 100644 index 000000000..2f12227fd --- /dev/null +++ b/app/assets/javascripts/templates/notes/new.jst.ejs @@ -0,0 +1,12 @@ +
+

<%- I18n.t('javascripts.notes.new.intro') %>

+
+ + + +
+
+ +
+
+
diff --git a/app/assets/javascripts/templates/notes/show.jst.ejs b/app/assets/javascripts/templates/notes/show.jst.ejs new file mode 100644 index 000000000..7063090df --- /dev/null +++ b/app/assets/javascripts/templates/notes/show.jst.ejs @@ -0,0 +1,33 @@ +
+

<%- I18n.t('javascripts.notes.show.title', { id: note.id }) %>

+ <% if (note.comments.some(function (comment) { return !comment.user })) { %> + <%- I18n.t('javascripts.notes.show.anonymous_warning') %> + <% } %> + <% note.comments.forEach(function (comment) { %> +
+ + <% if (comment.user) { %> + <%= I18n.t('javascripts.notes.show.' + comment.action + '_by', { + user: comment.user, user_url: comment.user_url, + time: I18n.l("time.formats.long", comment.date) + }) %> + <% } else { %> + <%- I18n.t('javascripts.notes.show.' + comment.action + '_by_anonymous', { + time: I18n.l("time.formats.long", comment.date) + }) %> + <% } %> + +
<%= comment.html %>
+
+ <% }) %> + <% if (note.status == "open") { %> +
+ +
+
+ + +
+
+ <% } %> +
diff --git a/app/assets/stylesheets/common.css.scss b/app/assets/stylesheets/common.css.scss index e57db18ce..d0dd894eb 100644 --- a/app/assets/stylesheets/common.css.scss +++ b/app/assets/stylesheets/common.css.scss @@ -537,6 +537,12 @@ table { background:#fff; } +.leaflet-control-attribution a.disabled { + color: #99c9dc; + cursor: default; + text-decoration: none; +} + .site-index .leaflet-top, .site-export .leaflet-top { top: 10px !important; @@ -550,6 +556,12 @@ table { left: 10px !important; } +.leaflet-popup-scrolled { + padding-right: 20px; + border-bottom: 0px !important; + border-top: 0px !important; +} + /* Rules for edit menu */ .menuicon { @@ -922,12 +934,20 @@ ul.results-list li { border-bottom: 1px solid #ccc; } &:first-child { margin-top: 0; } + &.warning { + color: #ff7070; + font-weight: bold; + } h4, p { margin-bottom: 5px; } p, ul, .bbox { margin-left: 33.3333%; } + ul p { + margin-left: 0; + margin-bottom: 0; + } h4 { width: 33.3333%; float: left; @@ -1488,6 +1508,9 @@ ul.secondary-actions { border-left: 0; padding-left: 0; } + &:last-child { + margin-right: 0px; + } } } @@ -1625,3 +1648,53 @@ a.button.submit { } } } + +/* Rules for the user notes list */ + +.note_list { + tr.creator { + background-color: #eeeeee; + } + + td { + padding: 3px; + } + + p { + margin-bottom: 0px; + } +} + +/* Rules for the notes interface */ + +.note { + h2 { + margin-bottom: 10px; + } + + .warning { + display: block; + background-color: #ff7070; + padding: 4px 6px; + margin-bottom: 10px; + } + + .comment_body { + margin-top: 4px; + margin-bottom: 4px; + + p { + margin-top: 0px; + margin-bottom: 0px; + } + } + + .comment { + width: 100%; + } + + .buttons { + margin-top: 5px; + text-align: right; + } +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4b2c70825..4ac3297c6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -55,6 +55,10 @@ class ApplicationController < ActionController::Base end end + def require_oauth + @oauth = @user.access_token(OAUTH_KEY) if @user and defined? OAUTH_KEY + end + ## # requires the user to be logged in by the token or HTTP methods, or have an # OAuth token with the right capability. this method is a bit of a pain to call diff --git a/app/controllers/browse_controller.rb b/app/controllers/browse_controller.rb index e5557280e..5526aacca 100644 --- a/app/controllers/browse_controller.rb +++ b/app/controllers/browse_controller.rb @@ -76,4 +76,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 index 000000000..b25dc9a9e --- /dev/null +++ b/app/controllers/notes_controller.rb @@ -0,0 +1,322 @@ +class NotesController < ApplicationController + + layout 'site', :only => [:mine] + + before_filter :check_api_readable + before_filter :authorize_web, :only => [:mine] + before_filter :setup_user_auth, :only => [:create, :comment] + before_filter :authorize, :only => [:close, :destroy] + before_filter :check_api_writable, :only => [:create, :comment, :close, :destroy] + 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") if params[:text].blank? + + # Extract the arguments + lon = params[:lon].to_f + lat = params[:lat].to_f + comment = params[:text] + + # 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? + + # Save the note + @note.save! + + # Add a comment to the note + add_comment(@note, comment, "opened") + end + + # Return a copy of the new note + respond_to do |format| + format.xml { render :action => :show } + format.json { render :action => :show } + end + 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") if params[:text].blank? + + # Extract the arguments + id = params[:id].to_i + comment = params[:text] + + # Find the note and check it is valid + @note = Note.find(id) + raise OSM::APINotFoundError unless @note + raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? + raise OSM::APINoteAlreadyClosedError.new(@note) if @note.closed? + + # Add a comment to the note + Note.transaction do + add_comment(@note, comment, "commented") + end + + # Return a copy of the updated note + respond_to do |format| + format.xml { render :action => :show } + format.json { render :action => :show } + end + 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 + comment = params[:text] + + # Find the note and check it is valid + @note = Note.find_by_id(id) + raise OSM::APINotFoundError unless @note + raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? + raise OSM::APINoteAlreadyClosedError.new(@note) if @note.closed? + + # Close the note and add a comment + Note.transaction do + @note.close + + add_comment(@note, comment, "closed") + end + + # Return a copy of the updated note + respond_to do |format| + format.xml { render :action => :show } + format.json { render :action => :show } + end + 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.new("note", @note.id) 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 + + # Find the note and check it is valid + note = Note.find(id) + raise OSM::APINotFoundError unless note + raise OSM::APIAlreadyDeletedError.new("note", note.id) unless note.visible? + + # Mark the note as hidden + Note.transaction do + note.status = "hidden" + note.save + + add_comment(note, nil, "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.subheading', :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, id").uniq.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, event) + attributes = { :visible => true, :event => event, :body => text } + + if @user + attributes[:author_id] = @user.id + else + attributes[:author_ip] = request.remote_ip + end + + comment = note.comments.create(attributes, :without_protection => true) + + note.comments.map { |c| c.author }.uniq.each do |user| + if user and user != @user + Notifier.note_comment_notification(comment, user).deliver + end + end + end +end diff --git a/app/controllers/site_controller.rb b/app/controllers/site_controller.rb index 606143f4f..1ea3f7cb0 100644 --- a/app/controllers/site_controller.rb +++ b/app/controllers/site_controller.rb @@ -5,6 +5,7 @@ class SiteController < ApplicationController before_filter :authorize_web before_filter :set_locale before_filter :require_user, :only => [:edit] + before_filter :require_oauth, :only => [:index] def index unless STATUS == :database_readonly or STATUS == :database_offline diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 49608c4a0..3771acf37 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -95,4 +95,8 @@ module ApplicationHelper I18n.t("html.dir") end end + + def friendly_date(date) + content_tag(:span, time_ago_in_words(date), :title => l(date, :format => :friendly)) + end end diff --git a/app/helpers/geocoder_helper.rb b/app/helpers/geocoder_helper.rb index f5519ddc3..8f662bde7 100644 --- a/app/helpers/geocoder_helper.rb +++ b/app/helpers/geocoder_helper.rb @@ -22,22 +22,6 @@ module GeocoderHelper end def describe_location(lat, lon, zoom = nil, language = nil) - zoom = zoom || 14 - language = language || request.user_preferred_languages.join(',') - url = "http://nominatim.openstreetmap.org/reverse?lat=#{lat}&lon=#{lon}&zoom=#{zoom}&accept-language=#{language}" - - begin - response = OSM::Timer.timeout(4) do - REXML::Document.new(Net::HTTP.get(URI.parse(url))) - end - rescue Exception - response = nil - end - - if response and result = response.get_text("reversegeocode/result") - result.to_s - else - "#{number_with_precision(lat, :precision => 3)}, #{number_with_precision(lon, :precision => 3)}" - end + Nominatim.describe_location(lat, lon, zoom, language) end end diff --git a/app/helpers/note_helper.rb b/app/helpers/note_helper.rb new file mode 100644 index 000000000..f2bff861f --- /dev/null +++ b/app/helpers/note_helper.rb @@ -0,0 +1,17 @@ +module NoteHelper + def note_event(at, by) + if by.nil? + I18n.t("browse.note.at_html", :when => friendly_date(at)).html_safe + else + I18n.t("browse.note.at_by_html", :when => friendly_date(at), :user => note_author(by)).html_safe + end + end + + def note_author(author, link_options = {}) + if author.nil? + "" + else + link_to h(author.display_name), link_options.merge({:controller => "user", :action => "view", :display_name => author.display_name}) + end + end +end diff --git a/app/models/note.rb b/app/models/note.rb new file mode 100644 index 000000000..bb56c5ce0 --- /dev/null +++ b/app/models/note.rb @@ -0,0 +1,76 @@ +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.display_name if comment.author + resp += " " + comment.created_at.to_s + " ]" + comment_no += 1 + end + + return resp + end + + # Check if a note is visible + def visible? + status != "hidden" + end + + # Check if a note is closed + def closed? + not closed_at.nil? + 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 + +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 index 000000000..07d43cd74 --- /dev/null +++ b/app/models/note_comment.rb @@ -0,0 +1,17 @@ +class NoteComment < ActiveRecord::Base + belongs_to :note, :foreign_key => :note_id, :touch => true + 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 comment text + def body + RichText.new("text", read_attribute(:body)) + end +end diff --git a/app/models/notifier.rb b/app/models/notifier.rb index 2a5bb15bc..5972a700b 100644 --- a/app/models/notifier.rb +++ b/app/models/notifier.rb @@ -6,7 +6,7 @@ class Notifier < ActionMailer::Base def signup_confirm(user, token) @locale = user.preferred_language_from(I18n.available_locales) - + # If we are passed an email address verification token, create # the confirumation URL for account activation. # @@ -19,7 +19,7 @@ class Notifier < ActionMailer::Base :display_name => user.display_name, :confirm_string => token.token) end - + mail :to => user.email, :subject => I18n.t('notifier.signup_confirm.subject', :locale => @locale) end @@ -67,7 +67,7 @@ class Notifier < ActionMailer::Base mail :to => trace.user.email, :subject => I18n.t('notifier.gpx_notification.failure.subject', :locale => @locale) end - + def message_notification(message) @locale = message.recipient.preferred_language_from(I18n.available_locales) @to_user = message.recipient.display_name @@ -123,6 +123,28 @@ 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) + @locale = recipient.preferred_language_from(I18n.available_locales) + @noteurl = browse_note_url(comment.note, :host => SERVER_URL) + @place = Nominatim.describe_location(comment.note.lat, comment.note.lon, 14, @locale) + @comment = comment.body + @owner = recipient == comment.note.author + + if comment.author + @commenter = comment.author.display_name + else + @commenter = I18n.t("notifier.note_comment_notification.anonymous") + end + + if @owner + subject = I18n.t('notifier.note_comment_notification.subject_own', :commenter => @commenter) + else + subject = I18n.t('notifier.note_comment_notification.subject_other', :commenter => @commenter) + end + + mail :to => recipient.email, :subject => subject + end + private def from_address(name, type, id, digest) diff --git a/app/models/user.rb b/app/models/user.rb index 2e312119f..778afbf8a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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] diff --git a/app/views/browse/_map.html.erb b/app/views/browse/_map.html.erb index c72040955..ed3cc0faf 100644 --- a/app/views/browse/_map.html.erb +++ b/app/views/browse/_map.html.erb @@ -2,7 +2,7 @@
- <% 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? %> <% content_for :head do %> <%= javascript_include_tag "browse" %> @@ -19,6 +19,13 @@ :maxlon => bbox.max_lon, :maxlat => bbox.max_lat } + elsif map.instance_of? Note + data = { + :type => "note", + :id => map.id, + :lon => map.lon, + :lat => map.lat + } else data = { :type => map.class.name.downcase, @@ -33,10 +40,17 @@
+ diff --git a/app/views/user/view.html.erb b/app/views/user/view.html.erb index 67619a536..cf74fc0e0 100644 --- a/app/views/user/view.html.erb +++ b/app/views/user/view.html.erb @@ -10,6 +10,9 @@ <%= link_to t('user.view.my edits'), :controller => 'changeset', :action => 'list', :display_name => @user.display_name %> <%= number_with_delimiter(@user.changesets.size) %> +
  • + <%= link_to t('user.view.my notes'), :controller => 'notes', :action=> 'mine' %> +
  • <%= link_to t('user.view.my traces'), :controller => 'trace', :action=>'mine' %> <%= number_with_delimiter(@user.traces.size) %> @@ -49,6 +52,9 @@ <%= link_to t('user.view.edits'), :controller => 'changeset', :action => 'list', :display_name => @this_user.display_name %> <%= number_with_delimiter(@this_user.changesets.size) %>
  • +
  • + <%= link_to t('user.view.notes'), :controller => 'notes', :action=> 'mine' %> +
  • <%= link_to t('user.view.traces'), :controller => 'trace', :action => 'list', :display_name => @this_user.display_name %> <%= number_with_delimiter(@this_user.traces.size) %> diff --git a/config/example.application.yml b/config/example.application.yml index 249e44773..30f367f1c 100644 --- a/config/example.application.yml +++ b/config/example.application.yml @@ -27,6 +27,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 @@ -74,6 +76,8 @@ defaults: &defaults default_editor: "potlatch2" # OAuth consumer key for Potlatch 2 #potlatch2_key: "" + # OAuth consumer key for the web site + #oauth_key: "" # Whether to require users to view the CTs before continuing to edit... require_terms_seen: false # Whether to require users to agree to the CTs before editing diff --git a/config/i18n-js.yml b/config/i18n-js.yml index 982d60da7..4882a4357 100644 --- a/config/i18n-js.yml +++ b/config/i18n-js.yml @@ -23,6 +23,8 @@ translations: - file: "app/assets/javascripts/i18n/translations.js" only: + - "*.date" + - "*.time" - "*.browse.start_rjs.*" - "*.export.start_rjs.*" - "*.javascripts.*" diff --git a/config/initializers/action_view.rb b/config/initializers/action_view.rb new file mode 100644 index 000000000..1789ec199 --- /dev/null +++ b/config/initializers/action_view.rb @@ -0,0 +1,32 @@ +# +# Make :formats work when rendering one partial from another +# +# Taken from https://github.com/rails/rails/pull/6626 +# +module ActionView + class AbstractRenderer #:nodoc: + def prepend_formats(formats) + formats = Array(formats) + return if formats.empty? + @lookup_context.formats = formats | @lookup_context.formats + end + end + + class PartialRenderer + def setup_with_formats(context, options, block) + prepend_formats(options[:formats]) + setup_without_formats(context, options, block) + end + + alias_method_chain :setup, :formats + end + + class TemplateRenderer + def render_with_formats(context, options) + prepend_formats(options[:formats]) + render_without_formats(context, options) + end + + alias_method_chain :render, :formats + end +end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index c676f6e46..be247274f 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -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 diff --git a/config/locales/de.yml b/config/locales/de.yml index 31ee8d845..338c8bbf1 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -953,6 +953,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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 58a62e52f..5d3e6bbae 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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:" @@ -156,11 +158,13 @@ en: node: "View node on larger map" way: "View way on larger map" relation: "View relation on larger map" + note: "View note on larger map" edit: area: "Edit area" node: "Edit node" way: "Edit way" relation: "Edit relation" + note: "Edit note" node_details: coordinates: "Coordinates:" part_of: "Part of:" @@ -220,6 +224,7 @@ en: download_xml: "Download XML" view_history: "View history" start_rjs: + notes_layer_name: "Browse Notes" data_layer_name: "Browse Map Data" data_frame_title: "Data" zoom_or_select: "Zoom in or select an area of the map to view" @@ -279,6 +284,16 @@ 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_html: "%{when} ago" + at_by_html: "%{when} ago by %{user}" + description: "Description:" + comments: "Comments:" changeset: changeset_paging_nav: showing_page: "Page %{page}" @@ -1191,6 +1206,14 @@ 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_comment_notification: + anonymous: An anonymous user + 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" @@ -1265,6 +1288,7 @@ en: js_2: "OpenStreetMap uses JavaScript for its slippy map." permalink: Permalink shortlink: Shortlink + createnote: Add a note license: copyright: "Copyright OpenStreetMap and contributors, under an open license" license_url: "http://openstreetmap.org/copyright" @@ -1675,6 +1699,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 @@ -1684,6 +1709,7 @@ en: diary: diary edits: edits traces: traces + notes: map notes remove as friend: unfriend add as friend: add friend mapper since: "Mapper since:" @@ -1954,6 +1980,33 @@ 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: "Created %{when} ago" + opened_at_by: "Created %{when} ago by %{user}" + commented_at: "Updated %{when} ago" + commented_at_by: "Updated %{when} ago by %{user}" + closed_at: "Resolved %{when} ago" + closed_at_by: "Resolved %{when} ago by %{user}" + reopened_at: "Reactivated %{when} ago" + 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" + subheading: "Notes submitted or commented on by %{user}" + id: "Id" + creator: "Creator" + description: "Description" + created_at: "Created at" + last_changed: "Last changed" + ago_html: "%{when} ago" javascripts: map: base: @@ -1970,6 +2023,27 @@ 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 + createnote_tooltip: Add a note to the map + createnote_disabled_tooltip: Zoom in to add a note to the map + createnote_zoom_alert: You must zoom in to add a note to the map + notes: + new: + intro: "In order to improve the map the information you enter is shown to other mappers, so please be as descriptive and precise as possible when moving the marker to the correct position and entering your note below." + add: Add Note + show: + title: Note %{id} + anonymous_warning: This note includes comments from anonymous users which should be independently verified. + opened_by: "created by %{user} at %{time}" + opened_by_anonymous: "created by anonymous at %{time}" + commented_by: "comment from %{user} at %{time}" + commented_by_anonymous: "comment from anonymous at %{time}" + closed_by: "resolved by %{user} at %{time}" + closed_by_anonymous: "resolved by anonymous at %{time}" + reopened_by: "reactivated by %{user} at %{time}" + reopened_by_anonymous: "reactivated by anonymous at %{time}" + resolve: Resolve + comment_and_resolve: Comment & Resolve + comment: Comment redaction: edit: description: "Description" diff --git a/config/openlayers.cfg b/config/openlayers.cfg index 34e19917a..6f6b778a8 100644 --- a/config/openlayers.cfg +++ b/config/openlayers.cfg @@ -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,16 @@ 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/QueryStringFilter.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 diff --git a/config/routes.rb b/config/routes.rb index 1cdaaea40..a4a8faf43 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -75,11 +75,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+/ @@ -89,8 +109,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+/, :as => "browse_note" 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 index 000000000..b3a1acee6 --- /dev/null +++ b/db/migrate/053_add_map_bug_tables.rb @@ -0,0 +1,33 @@ +require '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 index 000000000..b094cc593 --- /dev/null +++ b/db/migrate/054_refactor_map_bug_tables.rb @@ -0,0 +1,34 @@ +require '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 index 000000000..35186100d --- /dev/null +++ b/db/migrate/055_change_map_bug_comment_type.rb @@ -0,0 +1,11 @@ +require '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 index 000000000..bb4c19c1a --- /dev/null +++ b/db/migrate/056_add_date_closed.rb @@ -0,0 +1,11 @@ +require '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 index 000000000..f28aee7d8 --- /dev/null +++ b/db/migrate/057_add_map_bug_comment_event.rb @@ -0,0 +1,15 @@ +require '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 index 000000000..e7dfcb793 --- /dev/null +++ b/db/migrate/20110508145337_cleanup_bug_tables.rb @@ -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 index 000000000..2934b73fd --- /dev/null +++ b/db/migrate/20110521142405_rename_bugs_to_notes.rb @@ -0,0 +1,51 @@ +require '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_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_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_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_table :notes, :map_bugs + + rename_enumeration "note_event_enum", "map_bug_event_enum" + rename_enumeration "note_status_enum", "map_bug_status_enum" + end +end diff --git a/db/migrate/20121119165817_drop_nearby_place_from_notes.rb b/db/migrate/20121119165817_drop_nearby_place_from_notes.rb new file mode 100644 index 000000000..bbef25946 --- /dev/null +++ b/db/migrate/20121119165817_drop_nearby_place_from_notes.rb @@ -0,0 +1,9 @@ +class DropNearbyPlaceFromNotes < ActiveRecord::Migration + def up + remove_column :notes, :nearby_place + end + + def down + add_column :notes, :nearby_place, :string + end +end diff --git a/db/migrate/20121202155309_remove_author_name_from_note_comment.rb b/db/migrate/20121202155309_remove_author_name_from_note_comment.rb new file mode 100644 index 000000000..60d0ab23b --- /dev/null +++ b/db/migrate/20121202155309_remove_author_name_from_note_comment.rb @@ -0,0 +1,9 @@ +class RemoveAuthorNameFromNoteComment < ActiveRecord::Migration + def up + remove_column :note_comments, :author_name + end + + def down + add_column :note_comments, :author_name, :string + end +end diff --git a/db/migrate/20121203124841_change_note_address_to_inet.rb b/db/migrate/20121203124841_change_note_address_to_inet.rb new file mode 100644 index 000000000..e4e53e35a --- /dev/null +++ b/db/migrate/20121203124841_change_note_address_to_inet.rb @@ -0,0 +1,9 @@ +class ChangeNoteAddressToInet < ActiveRecord::Migration + def up + execute "ALTER TABLE note_comments ALTER COLUMN author_ip TYPE inet USING CAST(author_ip AS inet)" + end + + def down + change_column :note_comments, :author_ip, :string + end +end diff --git a/db/structure.sql b/db/structure.sql index 339533b92..b76995b68 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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/notes.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/notes.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/notes.osm.compton.nu/db/functions/libpgosm.so', 'xid_to_int4'; SET default_tablespace = ''; @@ -218,8 +242,8 @@ CREATE TABLE client_applications ( key character varying(50), secret character varying(50), user_id integer, - created_at timestamp without time zone, - updated_at timestamp without time zone, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, allow_read_prefs boolean DEFAULT false NOT NULL, allow_write_prefs boolean DEFAULT false NOT NULL, allow_write_diary boolean DEFAULT false NOT NULL, @@ -700,6 +724,76 @@ 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_ip inet, + 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, + 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: -- @@ -708,8 +802,8 @@ CREATE TABLE oauth_nonces ( id integer NOT NULL, nonce character varying(255), "timestamp" integer, - created_at timestamp without time zone, - updated_at timestamp without time zone + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL ); @@ -745,8 +839,8 @@ CREATE TABLE oauth_tokens ( secret character varying(50), authorized_at timestamp without time zone, invalidated_at timestamp without time zone, - created_at timestamp without time zone, - updated_at timestamp without time zone, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, allow_read_prefs boolean DEFAULT false NOT NULL, allow_write_prefs boolean DEFAULT false NOT NULL, allow_write_diary boolean DEFAULT false NOT NULL, @@ -874,8 +968,8 @@ CREATE TABLE user_blocks ( ends_at timestamp without time zone NOT NULL, needs_view boolean DEFAULT false NOT NULL, revoker_id bigint, - created_at timestamp without time zone, - updated_at timestamp without time zone, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, reason_format format_enum DEFAULT 'html'::format_enum NOT NULL ); @@ -917,8 +1011,8 @@ CREATE TABLE user_preferences ( CREATE TABLE user_roles ( id integer NOT NULL, user_id bigint NOT NULL, - created_at timestamp without time zone, - updated_at timestamp without time zone, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, role user_role_enum NOT NULL, granter_id bigint NOT NULL ); @@ -1000,9 +1094,9 @@ 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), changesets_count integer DEFAULT 0 NOT NULL, @@ -1160,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: - -- @@ -1377,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: -- @@ -1727,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: -- @@ -2061,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: - -- @@ -2245,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'); @@ -2273,6 +2445,12 @@ INSERT INTO schema_migrations (version) VALUES ('20121005195010'); INSERT INTO schema_migrations (version) VALUES ('20121012044047'); +INSERT INTO schema_migrations (version) VALUES ('20121119165817'); + +INSERT INTO schema_migrations (version) VALUES ('20121202155309'); + +INSERT INTO schema_migrations (version) VALUES ('20121203124841'); + INSERT INTO schema_migrations (version) VALUES ('21'); INSERT INTO schema_migrations (version) VALUES ('22'); @@ -2343,6 +2521,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'); diff --git a/lib/bounding_box.rb b/lib/bounding_box.rb index e560dbefa..38b994d26 100644 --- a/lib/bounding_box.rb +++ b/lib/bounding_box.rb @@ -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 diff --git a/lib/geo_record.rb b/lib/geo_record.rb index 0d010eb86..0c261dc69 100644 --- a/lib/geo_record.rb +++ b/lib/geo_record.rb @@ -45,4 +45,3 @@ private 180/Math::PI * Math.log(Math.tan(Math::PI/4+a*(Math::PI/180)/2)) end end - diff --git a/lib/migrate.rb b/lib/migrate.rb index 7549add2a..baa7faecd 100644 --- a/lib/migrate.rb +++ b/lib/migrate.rb @@ -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,10 @@ 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 end end end diff --git a/lib/nominatim.rb b/lib/nominatim.rb new file mode 100644 index 000000000..1d2037d8d --- /dev/null +++ b/lib/nominatim.rb @@ -0,0 +1,26 @@ +module Nominatim + extend ActionView::Helpers::NumberHelper + + def self.describe_location(lat, lon, zoom = nil, language = nil) + zoom = zoom || 14 + language = language || request.user_preferred_languages.join(',') + + Rails.cache.fetch "/nominatim/location/#{lat}/#{lon}/#{zoom}/#{language}" do + url = "http://nominatim.openstreetmap.org/reverse?lat=#{lat}&lon=#{lon}&zoom=#{zoom}&accept-language=#{language}" + + begin + response = OSM::Timer.timeout(4) do + REXML::Document.new(Net::HTTP.get(URI.parse(url))) + end + rescue Exception + response = nil + end + + if response and result = response.get_text("reversegeocode/result") + result.to_s + else + "#{number_with_precision(lat, :precision => 3)}, #{number_with_precision(lon, :precision => 3)}" + end + end + end +end diff --git a/lib/osm.rb b/lib/osm.rb index 3da9671c1..393011dac 100644 --- a/lib/osm.rb +++ b/lib/osm.rb @@ -281,6 +281,23 @@ module OSM end end + # Raised when the note provided is already closed + class APINoteAlreadyClosedError < APIError + def initialize(note) + @note = note + end + + attr_reader :note + + def status + :conflict + end + + def to_s + "The note #{@note.id} was closed at #{@note.closed_at}" + end + end + # Helper methods for going to/from mercator and lat/lng. class Mercator include Math diff --git a/test/fixtures/note_comments.yml b/test/fixtures/note_comments.yml new file mode 100644 index 000000000..881adb6ac --- /dev/null +++ b/test/fixtures/note_comments.yml @@ -0,0 +1,107 @@ +t1: + id: 1 + note_id: 1 + visible: true + created_at: 2007-01-01 00:00:00 + 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_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_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_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_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_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_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_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_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_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 index 000000000..ffecba869 --- /dev/null +++ b/test/fixtures/notes.yml @@ -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 diff --git a/test/functional/browse_controller_test.rb b/test/functional/browse_controller_test.rb index 9ba8dd8f0..87a3e7489 100644 --- a/test/functional/browse_controller_test.rb +++ b/test/functional/browse_controller_test.rb @@ -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 index 000000000..2b880943c --- /dev/null +++ b/test/functional/notes_controller_test.rb @@ -0,0 +1,484 @@ +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, :text => "This is a comment", :format => "json"} + end + end + 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 "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_nil js["properties"]["comments"].last["user"] + id = js["properties"]["id"] + + 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_nil 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, :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, :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} + 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, :text => ""} + 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, :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, :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, :text => "This is an additional comment", :format => "json"} + end + 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_nil js["properties"]["comments"].last["user"] + + 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_nil js["properties"]["comments"].last["user"] + end + + def test_note_comment_create_fail + assert_no_difference('NoteComment.count') do + post :comment, {: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} + end + assert_response :bad_request + + assert_no_difference('NoteComment.count') do + post :comment, {:id => notes(:open_note_with_comment).id, :text => ""} + end + assert_response :bad_request + + assert_no_difference('NoteComment.count') do + post :comment, {:id => 12345, :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, :text => "This is an additional comment"} + end + assert_response :gone + + assert_no_difference('NoteComment.count') do + post :comment, {:id => notes(:closed_note_with_comment).id, :text => "This is an additional comment"} + end + assert_response :conflict + end + + def test_note_close_success + post :close, {:id => notes(:open_note_with_comment).id, :text => "This is a close comment", :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 "This is a close comment", js["properties"]["comments"].last["text"] + assert_nil js["properties"]["comments"].last["user"] + + 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 "This is a close comment", js["properties"]["comments"].last["text"] + assert_nil 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 + + post :close, {:id => notes(:closed_note_with_comment).id} + assert_response :conflict + 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 + 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, {: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 diff --git a/vendor/assets/ohauth/ohauth.js b/vendor/assets/ohauth/ohauth.js new file mode 100644 index 000000000..0497da87c --- /dev/null +++ b/vendor/assets/ohauth/ohauth.js @@ -0,0 +1,86 @@ +(function(context) { + +var ohauth = {}; + +ohauth.qsString = function(obj) { + return Object.keys(obj).sort().map(function(key) { + return encodeURIComponent(key) + '=' + + encodeURIComponent(obj[key]); + }).join('&'); +}; + +ohauth.sha = sha1(); + +ohauth.stringQs = function(str) { + return str.split('&').reduce(function(obj, pair){ + var parts = pair.split('='); + obj[parts[0]] = (null === parts[1]) ? + '' : decodeURIComponent(parts[1]); + return obj; + }, {}); +}; + +ohauth.rawxhr = function(method, url, data, headers, callback) { + var xhr = new XMLHttpRequest(), twoHundred = /^20\d$/; + xhr.onreadystatechange = function() { + if (4 == xhr.readyState && 0 !== xhr.status) { + if (twoHundred.test(xhr.status)) callback(null, xhr); + else return callback(xhr, null); + } + }; + xhr.onerror = function(e) { return callback(e, null); }; + xhr.open(method, url, true); + for (var h in headers) xhr.setRequestHeader(h, headers[h]); + xhr.send(data); +}; + +ohauth.xhr = function(method, url, auth, data, options, callback) { + var headers = (options && options.header) || { + 'Content-Type': 'application/x-www-form-urlencoded' + }; + headers.Authorization = 'OAuth ' + ohauth.authHeader(auth); + ohauth.rawxhr(method, url, auth, data, headers, callback); +}; + +ohauth.nonce = function() { + for (var o = ''; o.length < 6;) { + o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)]; + } + return o; +}; + +ohauth.authHeader = function(obj) { + return Object.keys(obj).sort().map(function(key) { + return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"'; + }).join(', '); +}; + +ohauth.timestamp = function() { return ~~((+new Date()) / 1000); }; + +ohauth.percentEncode = function(s) { + return encodeURIComponent(s) + .replace(/\!/g, '%21').replace(/\'/g, '%27') + .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29'); +}; + +ohauth.baseString = function(method, url, params) { + if (params.oauth_signature) delete params.oauth_signature; + return [ + method, + ohauth.percentEncode(url), + ohauth.percentEncode(ohauth.qsString(params))].join('&'); +}; + +ohauth.signature = function(oauth_secret, token_secret, baseString) { + return ohauth.sha.b64_hmac_sha1( + ohauth.percentEncode(oauth_secret) + '&' + + ohauth.percentEncode(token_secret), + baseString); +}; + +context.ohauth = ohauth; + +// export for npm/browserify compatibility +if (typeof module !== 'undefined') module.exports = ohauth; + +})(this); diff --git a/vendor/assets/ohauth/sha.js b/vendor/assets/ohauth/sha.js new file mode 100644 index 000000000..5b6aa65ba --- /dev/null +++ b/vendor/assets/ohauth/sha.js @@ -0,0 +1,191 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +function sha1() { + + /* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ + var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ + var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */ + var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + + /* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ + function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} + function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} + function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} + function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} + function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} + function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + + /* + * Perform a simple self-test to see if the VM is working + */ + function sha1_vm_test() { + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; + } + + /* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ + function core_sha1(x, len) { + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for(var i = 0; i < x.length; i += 16) { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for(var j = 0; j < 80; j++) { + if(j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); + + } + + /* + * Perform the appropriate triplet combination function for the current + * iteration + */ + function sha1_ft(t, b, c, d) { + if(t < 20) return (b & c) | ((~b) & d); + if(t < 40) return b ^ c ^ d; + if(t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; + } + + /* + * Determine the appropriate additive constant for the current iteration + */ + function sha1_kt(t) { + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; + } + + /* + * Calculate the HMAC-SHA1 of a key and some data + */ + function core_hmac_sha1(key, data) { + var bkey = str2binb(key); + if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); + } + + /* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ + function safe_add(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + /* + * Bitwise rotate a 32-bit number to the left. + */ + function rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + } + + /* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ + function str2binb(str) { + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); + return bin; + } + + /* + * Convert an array of big-endian words to a string + */ + function binb2str(bin) { + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); + return str; + } + + /* + * Convert an array of big-endian words to a hex string. + */ + function binb2hex(binarray) { + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; + } + + /* + * Convert an array of big-endian words to a base-64 string + */ + function binb2b64(binarray) { + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; + } + + return { b64_hmac_sha1: b64_hmac_sha1 }; +} + diff --git a/vendor/assets/openlayers/OpenLayers.js b/vendor/assets/openlayers/OpenLayers.js index ae17272ad..ad5df5a32 100644 --- a/vendor/assets/openlayers/OpenLayers.js +++ b/vendor/assets/openlayers/OpenLayers.js @@ -809,7 +809,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.

    "+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.

    "+"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.

    "+"Developers: For help getting this working correctly, "+"click here",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.

    "+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.

    "+"Most likely, this is because the ${layerLib} library "+"script was not correctly included.

    "+"Developers: For help getting this working correctly, "+"click here",'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.

    "+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.

    "+"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.

    "+"Developers: For help getting this working correctly, "+"click here",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.

    "+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.

    "+"Most likely, this is because the ${layerLib} library "+"script was not correctly included.

    "+"Developers: For help getting this working correctly, "+"click here",'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;i1){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;i1){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 +917,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.

    "+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.

    "+"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.

    "+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"clicca qui",'getLayerWarning':"Il livello ${layerType} non è riuscito a caricare correttamente.

    "+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.

    "+"Più precisamente, ciò accade perchè la libreria ${layerLib} "+"non è stata inclusa nella pagina.

    "+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"clicca qui",'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.

    "+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.

    "+"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.

    "+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"clicca qui",'getLayerWarning':"Il livello ${layerType} non è riuscito a caricare correttamente.

    "+"Per evitare questo messaggio, seleziona un nuovo BaseLayer "+"nel selettore di livelli nell'angolo in alto a destra.

    "+"Più precisamente, ciò accade perchè la libreria ${layerLib} "+"non è stata inclusa nella pagina.

    "+"Sviluppatori: Per aiuto su come farlo funzionare correttamente, "+"clicca qui",'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;i0){appliedRules=true;for(var i=0,len=elseRules.length;i=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();if(resp.code==OpenLayers.Protocol.Response.SUCCESS){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-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";} @@ -1044,7 +1074,11 @@ OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this,arguments);},onD layer.drawFeature(this.handles[i],this.renderIntent);}},transformFeature:function(mods){if(!this._setfeature){this.scale*=(mods.scale||1);this.ratio*=(mods.ratio||1);var oldRotation=this.rotation;this.rotation=(this.rotation+(mods.rotation||0))%360;if(this.events.triggerEvent("beforetransform",mods)!==false){var feature=this.feature;var geom=feature.geometry;var center=this.center;geom.rotate(-oldRotation,center);if(mods.scale||mods.ratio){geom.resize(mods.scale,center,mods.ratio);}else if(mods.center){feature.move(mods.center.getBounds().getCenterLonLat());} geom.rotate(this.rotation,center);this.layer.drawFeature(feature);feature.toState(OpenLayers.State.UPDATE);this.events.triggerEvent("transform",mods);}} this.layer.drawFeature(this.box,this.renderIntent);this.drawHandles();},destroy:function(){var geom;for(var i=0;i<8;++i){geom=this.box.geometry.components[i];geom._handle.destroy();geom._handle=null;geom._rotationHandle&&geom._rotationHandle.destroy();geom._rotationHandle=null;} -this.center=null;this.feature=null;this.handles=null;this.rotationHandleSymbolizer=null;this.rotationHandles=null;this.box.destroy();this.box=null;this.layer=null;this.dragControl.destroy();this.dragControl=null;OpenLayers.Control.prototype.destroy.apply(this,arguments);},CLASS_NAME:"OpenLayers.Control.TransformFeature"});OpenLayers.Lang["ia"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Le responsa a un requesta non esseva maneate: ${statusText}",'Permalink':"Permaligamine",'Overlays':"Superpositiones",'Base Layer':"Strato de base",'noFID':"Non pote actualisar un elemento sin FID.",'browserNotSupported':"Tu navigator non supporta le rendition de vectores. Le renditores actualmente supportate es:\n${renderers}",'minZoomLevelError':"Le proprietate minZoomLevel es solmente pro uso con le stratos descendente de FixedZoomLevels. Le facto que iste strato WFS verifica minZoomLevel es un reliquia del passato. Nonobstante, si nos lo remove immediatemente, nos pote rumper applicationes a base de OL que depende de illo. Ergo nos lo declara obsolete; le verification de minZoomLevel in basso essera removite in version 3.0. Per favor usa in su loco le configuration de resolutiones min/max como describite a: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transaction WFS: SUCCESSO ${response}",'commitFailed':"Transaction WFS: FALLEVA ${response}",'googleWarning':"Le strato Google non poteva esser cargate correctemente.\x3cbr\x3e\x3cbr\x3ePro disfacer te de iste message, selige un nove BaseLayer in le selector de strato in alto a dextra.\x3cbr\x3e\x3cbr\x3eMulto probabilemente, isto es proque le script del libreria de Google Maps non esseva includite o non contine le clave API correcte pro tu sito.\x3cbr\x3e\x3cbr\x3eDisveloppatores: Pro adjuta de corriger isto, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eclicca hic\x3c/a",'getLayerWarning':"Le strato ${layerType} non poteva esser cargate correctemente.\x3cbr\x3e\x3cbr\x3ePro disfacer te de iste message, selige un nove BaseLayer in le selector de strato in alto a dextra.\x3cbr\x3e\x3cbr\x3eMulto probabilemente, isto es proque le script del libreria de ${layerLib} non esseva correctemente includite.\x3cbr\x3e\x3cbr\x3eDisveloppatores: Pro adjuta de corriger isto, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eclicca hic\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Scala = 1 : ${scaleDenom}",'W':"W",'E':"E",'N':"N",'S':"S",'reprojectDeprecated':"Tu usa le option \'reproject\' in le strato ${layerName} layer. Iste option es obsolescente: illo esseva pro poter monstrar datos super cartas de base commercial, ma iste functionalitate pote ora esser attingite con le uso de Spherical Mercator. Ulterior information es disponibile a http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Iste methodo ha essite declarate obsolescente e essera removite in version 3.0. Per favor usa ${newMethod} in su loco."});OpenLayers.Handler.Box=OpenLayers.Class(OpenLayers.Handler,{dragHandler:null,boxDivClassName:'olHandlerBoxZoomBox',boxOffsets:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.dragHandler=new OpenLayers.Handler.Drag(this,{down:this.startBox,move:this.moveBox,out:this.removeBox,up:this.endBox},{keyMask:this.keyMask});},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);if(this.dragHandler){this.dragHandler.destroy();this.dragHandler=null;}},setMap:function(map){OpenLayers.Handler.prototype.setMap.apply(this,arguments);if(this.dragHandler){this.dragHandler.setMap(map);}},startBox:function(xy){this.callback("start",[]);this.zoomBox=OpenLayers.Util.createDiv('zoomBox',{x:-9999,y:-9999});this.zoomBox.className=this.boxDivClassName;this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.viewPortDiv.appendChild(this.zoomBox);OpenLayers.Element.addClass(this.map.viewPortDiv,"olDrawBox");},moveBox:function(xy){var startX=this.dragHandler.start.x;var startY=this.dragHandler.start.y;var deltaX=Math.abs(startX-xy.x);var deltaY=Math.abs(startY-xy.y);var offset=this.getBoxOffsets();this.zoomBox.style.width=(deltaX+offset.width+1)+"px";this.zoomBox.style.height=(deltaY+offset.height+1)+"px";this.zoomBox.style.left=(xy.x5||Math.abs(this.dragHandler.start.y-end.y)>5){var start=this.dragHandler.start;var top=Math.min(start.y,end.y);var bottom=Math.max(start.y,end.y);var left=Math.min(start.x,end.x);var right=Math.max(start.x,end.x);result=new OpenLayers.Bounds(left,bottom,right,top);}else{result=this.dragHandler.start.clone();} +this.center=null;this.feature=null;this.handles=null;this.rotationHandleSymbolizer=null;this.rotationHandles=null;this.box.destroy();this.box=null;this.layer=null;this.dragControl.destroy();this.dragControl=null;OpenLayers.Control.prototype.destroy.apply(this,arguments);},CLASS_NAME:"OpenLayers.Control.TransformFeature"});OpenLayers.Filter.Logical=OpenLayers.Class(OpenLayers.Filter,{filters:null,type:null,initialize:function(options){this.filters=[];OpenLayers.Filter.prototype.initialize.apply(this,[options]);},destroy:function(){this.filters=null;OpenLayers.Filter.prototype.destroy.apply(this);},evaluate:function(context){var i,len;switch(this.type){case OpenLayers.Filter.Logical.AND:for(i=0,len=this.filters.length;i5||Math.abs(this.dragHandler.start.y-end.y)>5){var start=this.dragHandler.start;var top=Math.min(start.y,end.y);var bottom=Math.max(start.y,end.y);var left=Math.min(start.x,end.x);var right=Math.max(start.x,end.x);result=new OpenLayers.Bounds(left,bottom,right,top);}else{result=this.dragHandler.start.clone();} this.removeBox();this.callback("done",[result]);},removeBox:function(){this.map.viewPortDiv.removeChild(this.zoomBox);this.zoomBox=null;this.boxOffsets=null;OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDrawBox");},activate:function(){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.dragHandler.activate();return true;}else{return false;}},deactivate:function(){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){if(this.dragHandler.deactivate()){if(this.zoomBox){this.removeBox();}} return true;}else{return false;}},getBoxOffsets:function(){if(!this.boxOffsets){var testDiv=document.createElement("div");testDiv.style.position="absolute";testDiv.style.border="1px solid black";testDiv.style.width="3px";document.body.appendChild(testDiv);var w3cBoxModel=testDiv.clientWidth==3;document.body.removeChild(testDiv);var left=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-left-width"));var right=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-right-width"));var top=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-top-width"));var bottom=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-bottom-width"));this.boxOffsets={left:left,right:right,top:top,bottom:bottom,width:w3cBoxModel===false?left+right:0,height:w3cBoxModel===false?top+bottom:0};} return this.boxOffsets;},CLASS_NAME:"OpenLayers.Handler.Box"});OpenLayers.Control.ZoomBox=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,out:false,keyMask:null,alwaysZoom:false,draw:function(){this.handler=new OpenLayers.Handler.Box(this,{done:this.zoomBox},{keyMask:this.keyMask});},zoomBox:function(position){if(position instanceof OpenLayers.Bounds){var bounds;if(!this.out){var minXY=this.map.getLonLatFromPixel({x:position.left,y:position.bottom});var maxXY=this.map.getLonLatFromPixel({x:position.right,y:position.top});bounds=new OpenLayers.Bounds(minXY.lon,minXY.lat,maxXY.lon,maxXY.lat);}else{var pixWidth=Math.abs(position.right-position.left);var pixHeight=Math.abs(position.top-position.bottom);var zoomFactor=Math.min((this.map.size.h/pixHeight),(this.map.size.w/pixWidth));var extent=this.map.getExtent();var center=this.map.getLonLatFromPixel(position.getCenterPixel());var xmin=center.lon-(extent.getWidth()/2)*zoomFactor;var xmax=center.lon+(extent.getWidth()/2)*zoomFactor;var ymin=center.lat-(extent.getHeight()/2)*zoomFactor;var ymax=center.lat+(extent.getHeight()/2)*zoomFactor;bounds=new OpenLayers.Bounds(xmin,ymin,xmax,ymax);} @@ -1138,9 +1172,78 @@ 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.

    "+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.

    "+"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.

    "+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"haga clic aquí",'getLayerWarning':"La capa ${layerType} no pudo ser cargada correctamente.

    "+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.

    "+"Probablemente, esto se debe a que el script de "+"la biblioteca ${layerLib} "+"no fue correctamente incluido en su página.

    "+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"haga clic aquí",'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.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;i0){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.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
    "+"Aby pozbyć się tej wiadomości, wybierz nową Warstwe podstawową "+"w przełączniku warstw w górnym prawym rogu mapy.

    "+"Najprawdopodobniej jest to spowodowane tym, że biblioteka Google Maps "+"nie jest załadowana, lub nie zawiera poprawnego klucza do API dla twojej strony

    "+"Programisto: Aby uzyskać pomoc , "+"kliknij tutaj",'getLayerWarning':"Warstwa ${layerType} nie mogła zostać załadowana poprawnie.

    "+"Aby pozbyć się tej wiadomości, wybierz nową Warstwe podstawową "+"w przełączniku warstw w górnym prawym rogu mapy.

    "+"Najprawdopodobniej jest to spowodowane tym, że biblioteka ${layerLib} "+"nie jest załadowana, lub może(o ile biblioteka tego wymaga) "+"byc potrzebny klucza do API dla twojej strony

    "+"Programisto: Aby uzyskać pomoc , "+"kliknij tutaj",'Scale = 1 : ${scaleDenom}':"Skala = 1 : ${scaleDenom}",'W':'ZACH','E':'WSCH','N':'PN','S':'PD','Graticule':'Siatka','reprojectDeprecated':"w warstwie ${layerName} używasz opcji 'reproject'. "+"Ta opcja jest przestarzała: "+"jej zastosowanie został zaprojektowany, aby wspierać wyświetlania danych przez komercyjne mapy, "+"jednak obecnie ta funkcjonalność powinien zostać osiągnięty za pomocą Spherical Mercator "+"its use was designed to support displaying data over commercial. Więcje informacji na ten temat możesz znaleźć na stronie "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Ta metoda jest przestarzała i będzie usunięta od wersji 3.0. "+"W zamian użyj ${newMethod}.",'proxyNeeded':"Prawdopodobnie musisz ustawić OpenLayers.ProxyHost aby otrzymać dostęp do ${url}."+"See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost"});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.

    "+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.

    "+"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.

    "+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"haga clic aquí",'getLayerWarning':"La capa ${layerType} no pudo ser cargada correctamente.

    "+"Para evitar este mensaje, seleccione una nueva Capa Base "+"en el selector de capas en la esquina superior derecha.

    "+"Probablemente, esto se debe a que el script de "+"la biblioteca ${layerLib} "+"no fue correctamente incluido en su página.

    "+"Desarrolladores: Para ayudar a hacer funcionar esto correctamente, "+"haga clic aquí",'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';} @@ -1169,7 +1272,19 @@ return true;},touchend:function(evt){if(this.started){this.started=false;this.pi return true;},activate:function(){var activated=false;if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.pinching=false;activated=true;} return activated;},deactivate:function(){var deactivated=false;if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){this.started=false;this.pinching=false;this.start=null;this.last=null;deactivated=true;} return deactivated;},getDistance:function(touches){var t0=touches[0];var t1=touches[1];return Math.sqrt(Math.pow(t0.clientX-t1.clientX,2)+ -Math.pow(t0.clientY-t1.clientY,2));},getPinchData:function(evt){var distance=this.getDistance(evt.touches);var scale=distance/this.start.distance;return{distance:distance,delta:this.last.distance-distance,scale:scale};},CLASS_NAME:"OpenLayers.Handler.Pinch"});OpenLayers.Lang['lt']=OpenLayers.Util.applyDefaults({'unhandledRequest':"Neapdorota užklausa gražino ${statusText}",'Permalink':"Pastovi nuoroda",'Overlays':"Papildomi sluoksniai",'Base Layer':"Pagrindinis sluoksnis",'noFID':"Negaliu atnaujinti objekto, kuris neturi FID.",'browserNotSupported':"Jūsų naršyklė nemoka parodyti vektorių. Šiuo metu galima naudotis tokiais rodymo varikliais:\n{renderers}",'commitSuccess':"WFS Tranzakcija: PAVYKO ${response}",'commitFailed':"WFS Tranzakcija: ŽLUGO ${response}",'Scale = 1 : ${scaleDenom}':"Mastelis = 1 : ${scaleDenom}",'W':'V','E':'R','N':'Š','S':'P','Graticule':'Tinklelis','methodDeprecated':"Šis metodas yra pasenęs ir 3.0 versijoje bus pašalintas. "+"Prašome naudoti ${newMethod}.",'end':''});OpenLayers.Lang.ca={'unhandledRequest':"Resposta a petició no gestionada ${statusText}",'Permalink':"Enllaç permanent",'Overlays':"Capes addicionals",'Base Layer':"Capa Base",'noFID':"No es pot actualitzar un element per al que no existeix FID.",'browserNotSupported':"El seu navegador no suporta renderització vectorial. Els renderitzadors suportats actualment són:\n${renderers}",'minZoomLevelError':"La propietat minZoomLevel s'ha d'utilitzar només "+"amb les capes que tenen FixedZoomLevels. El fet que "+"una capa wfs comprovi minZoomLevel és una relíquia del "+"passat. No podem, però, eliminar-la sense trencar "+"les aplicacions d'OpenLayers que en puguin dependre. "+"Així doncs estem fent-la obsoleta -- la comprovació "+"minZoomLevel s'eliminarà a la versió 3.0. Feu servir "+"els paràmetres min/max resolution en substitució, tal com es descriu aquí: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transacció WFS: CORRECTA ${response}",'commitFailed':"Transacció WFS: HA FALLAT ${response}",'googleWarning':"La capa Google no s'ha pogut carregar correctament.

    "+"Per evitar aquest missatge, seleccioneu una nova Capa Base "+"al gestor de capes de la cantonada superior dreta.

    "+"Probablement això és degut a que l'script de la biblioteca de "+"Google Maps no ha estat inclòs a la vostra pàgina, o no "+"conté la clau de l'API correcta per a la vostra adreça.

    "+"Desenvolupadors: Per obtenir consells sobre com fer anar això, "+"féu clic aquí",'getLayerWarning':"Per evitar aquest missatge, seleccioneu una nova Capa Base "+"al gestor de capes de la cantonada superior dreta.

    "+"Probablement això és degut a que l'script de la biblioteca "+"${layerLib} "+"no ha estat inclòs a la vostra pàgina.

    "+"Desenvolupadors: Per obtenir consells sobre com fer anar això, "+"féu clic aquí",'Scale = 1 : ${scaleDenom}':"Escala = 1 : ${scaleDenom}",'W':'O','E':'E','N':'N','S':'S','Graticule':'Retícula','reprojectDeprecated':"Esteu fent servir l'opció 'reproject' a la capa "+"${layerName}. Aquesta opció és obsoleta: el seu ús fou concebut "+"per suportar la visualització de dades sobre mapes base comercials, "+"però ara aquesta funcionalitat s'hauria d'assolir mitjançant el suport "+"de la projecció Spherical Mercator. Més informació disponible a "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Aquest mètode és obsolet i s'eliminarà a la versió 3.0. "+"Si us plau feu servir em mètode alternatiu ${newMethod}.",'end':''};OpenLayers.Lang["id"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Permintaan yang tak tertangani menghasilkan ${statusText}",'Permalink':"Pranala permanen",'Overlays':"Hamparan",'Base Layer':"Lapisan Dasar",'noFID':"Tidak dapat memperbarui fitur yang tidak memiliki FID.",'browserNotSupported':"Peramban Anda tidak mendukung penggambaran vektor. Penggambar yang didukung saat ini adalah:\n${renderers}",'minZoomLevelError':"Properti minZoomLevel hanya ditujukan bekerja dengan lapisan FixedZoomLevels-descendent. Pengecekan minZoomLevel oleh lapisan wfs adalah peninggalan masa lalu. Kami tidak dapat menghapusnya tanpa kemungkinan merusak aplikasi berbasis OL yang mungkin bergantung padanya. Karenanya, kami menganggapnya tidak berlaku -- Cek minZoomLevel di bawah ini akan dihapus pada 3.0. Silakan gunakan penyetelan resolusi min/maks seperti dijabarkan di sini: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaksi: BERHASIL ${respon}",'commitFailed':"WFS Transaksi: GAGAL ${respon}",'googleWarning':"Lapisan Google tidak dapat dimuat dengan benar.\x3cbr\x3e\x3cbr\x3eUntuk menghilangkan pesan ini, pilih suatu BaseLayer baru melalui penukar lapisan (layer switcher) di ujung kanan atas.\x3cbr\x3e\x3cbr\x3eKemungkinan besar ini karena pustaka skrip Google Maps tidak disertakan atau tidak mengandung kunci API yang tepat untuk situs Anda.\x3cbr\x3e\x3cbr\x3ePengembang: Untuk bantuan mengatasi masalah ini, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eklik di sini\x3c/a\x3e",'getLayerWarning':"Lapisan ${layerType} tidak dapat dimuat dengan benar.\x3cbr\x3e\x3cbr\x3eUntuk menghilangkan pesan ini, pilih suatu BaseLayer baru melalui penukar lapisan (layer switcher) di ujung kanan atas.\x3cbr\x3e\x3cbr\x3eKemungkinan besar ini karena pustaka skrip Google Maps tidak disertakan dengan benar.\x3cbr\x3e\x3cbr\x3ePengembang: Untuk bantuan mengatasi masalah ini, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eklik di sini\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Sekala = 1 : ${scaleDenom}",'W':"B",'E':"T",'N':"U",'S':"S",'reprojectDeprecated':"Anda menggunakan opsi \'reproject\' pada lapisan ${layerName}. Opsi ini telah ditinggalkan: penggunaannya dirancang untuk mendukung tampilan data melalui peta dasar komersial, tapi fungsionalitas tersebut saat ini harus dilakukan dengan menggunakan dukungan Spherical Mercator. Informasi lebih lanjut tersedia di http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Metode ini telah usang dan akan dihapus di 3.0. Sebaliknya, harap gunakan ${newMethod}."});OpenLayers.Lang["km"]=OpenLayers.Util.applyDefaults({'Permalink':"តំណភ្ជាប់អចិន្ត្រៃយ៍",'Base Layer':"ស្រទាប់បាត​",'Scale = 1 : ${scaleDenom}':"មាត្រដ្ឋាន = ១ ៖ ${scaleDenom}"});OpenLayers.Lang["nl"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Het verzoek is niet afgehandeld met de volgende melding: ${statusText}",'Permalink':"Permanente verwijzing",'Overlays':"Overlays",'Base Layer':"Achtergrondkaart",'noFID':"Een optie die geen FID heeft kan niet bijgewerkt worden.",'browserNotSupported':"Uw browser ondersteunt het weergeven van vectoren niet.\nMomenteel ondersteunde weergavemogelijkheden:\n${renderers}",'minZoomLevelError':"De eigenschap minZoomLevel is alleen bedoeld voor gebruik lagen met die afstammen van FixedZoomLevels-lagen.\nDat deze WFS-laag minZoomLevel controleert, is een overblijfsel uit het verleden.\nWe kunnen deze controle echter niet verwijderen zonder op OL gebaseerde applicaties die hervan afhankelijk zijn stuk te maken.\nDaarom heeft deze functionaliteit de eigenschap \'deprecated\' gekregen - de minZoomLevel wordt verwijderd in versie 3.0.\nGebruik in plaats van deze functie de mogelijkheid om min/max voor resolutie in te stellen zoals op de volgende pagina wordt beschreven:\nhttp://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS-transactie: succesvol ${response}",'commitFailed':"WFS-transactie: mislukt ${response}",'googleWarning':"De Google-Layer kon niet correct geladen worden.\x3cbr /\x3e\x3cbr /\x3e\nOm deze melding niet meer te krijgen, moet u een andere achtergrondkaart kiezen in de laagwisselaar in de rechterbovenhoek.\x3cbr /\x3e\x3cbr /\x3e\nDit komt waarschijnlijk doordat de bibliotheek ${layerLib} niet correct ingevoegd is.\x3cbr /\x3e\x3cbr /\x3e\nOntwikkelaars: \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eklik hier\x3c/a\x3e om dit werkend te krijgen.",'getLayerWarning':"De laag ${layerType} kon niet goed geladen worden.\x3cbr /\x3e\x3cbr /\x3e\nOm deze melding niet meer te krijgen, moet u een andere achtergrondkaart kiezen in de laagwisselaar in de rechterbovenhoek.\x3cbr /\x3e\x3cbr /\x3e\nDit komt waarschijnlijk doordat de bibliotheek ${layerLib} niet correct is ingevoegd.\x3cbr /\x3e\x3cbr /\x3e\nOntwikkelaars: \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eklik hier\x3c/a\x3e om dit werkend te krijgen.",'Scale = 1 : ${scaleDenom}':"Schaal = 1 : ${scaleDenom}",'W':"W",'E':"O",'N':"N",'S':"Z",'reprojectDeprecated':"U gebruikt de optie \'reproject\' op de laag ${layerName}.\nDeze optie is vervallen: deze optie was ontwikkeld om gegevens over commerciële basiskaarten weer te geven, maar deze functionaliteit wordt nu bereikt door ondersteuning van Spherical Mercator.\nMeer informatie is beschikbaar op http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Deze methode is verouderd en wordt verwijderd in versie 3.0.\nGebruik ${newMethod}."});OpenLayers.Lang["fr"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Requête non gérée, retournant ${statusText}",'Permalink':"Permalien",'Overlays':"Calques",'Base Layer':"Calque de base",'noFID':"Impossible de mettre à jour un objet sans identifiant (fid).",'browserNotSupported':"Votre navigateur ne supporte pas le rendu vectoriel. Les renderers actuellement supportés sont : \n${renderers}",'minZoomLevelError':"La propriété minZoomLevel doit seulement être utilisée pour des couches FixedZoomLevels-descendent. Le fait que cette couche WFS vérifie la présence de minZoomLevel est une relique du passé. Nous ne pouvons toutefois la supprimer sans casser des applications qui pourraient en dépendre. C\'est pourquoi nous la déprécions -- la vérification du minZoomLevel sera supprimée en version 3.0. A la place, merci d\'utiliser les paramètres de résolutions min/max tel que décrit sur : http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transaction WFS : SUCCES ${response}",'commitFailed':"Transaction WFS : ECHEC ${response}",'googleWarning':"La couche Google n\'a pas été en mesure de se charger correctement.\x3cbr\x3e\x3cbr\x3ePour supprimer ce message, choisissez une nouvelle BaseLayer dans le sélecteur de couche en haut à droite.\x3cbr\x3e\x3cbr\x3eCela est possiblement causé par la non-inclusion de la librairie Google Maps, ou alors parce que la clé de l\'API ne correspond pas à votre site.\x3cbr\x3e\x3cbr\x3eDéveloppeurs : pour savoir comment corriger ceci, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3ecliquez ici\x3c/a\x3e",'getLayerWarning':"La couche ${layerType} n\'est pas en mesure de se charger correctement.\x3cbr\x3e\x3cbr\x3ePour supprimer ce message, choisissez une nouvelle BaseLayer dans le sélecteur de couche en haut à droite.\x3cbr\x3e\x3cbr\x3eCela est possiblement causé par la non-inclusion de la librairie ${layerLib}.\x3cbr\x3e\x3cbr\x3eDéveloppeurs : pour savoir comment corriger ceci, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3ecliquez ici\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Echelle ~ 1 : ${scaleDenom}",'W':"O",'E':"E",'N':"N",'S':"S",'reprojectDeprecated':"Vous utilisez l\'option \'reproject\' sur la couche ${layerName}. Cette option est dépréciée : Son usage permettait d\'afficher des données au dessus de couches raster commerciales.Cette fonctionalité est maintenant supportée en utilisant le support de la projection Mercator Sphérique. Plus d\'information est disponible sur http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Cette méthode est dépréciée, et sera supprimée à la version 3.0. Merci d\'utiliser ${newMethod} à la place.",'proxyNeeded':"Vous avez très probablement besoin de renseigner OpenLayers.ProxyHost pour accéder à ${url}. Voir http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost"});OpenLayers.Lang["ksh"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Met dä Antwoot op en Aanfrooch ham_mer nix aanjefange: ${statusText}",'Permalink':"Lengk op Duuer",'Overlays':"Drövver jelaat",'Base Layer':"Jrund-Nivoh",'noFID':"En Saach, woh kein \x3ci lang=\"en\"\x3eFID\x3c/i\x3e för doh es, löht sesch nit ändere.",'browserNotSupported':"Dinge Brauser kann kein Väktore ußjävve. De Zoote Ußjaabe, di em Momang jon, sen:\n${renderers}",'minZoomLevelError':"De Eijeschaff „\x3ccode lang=\"en\"\x3eminZoomLevel\x3c/code\x3e“ es bloß doför jedaach, dat mer se met dä Nivvohß bruch, di vun \x3ccode lang=\"en\"\x3eFixedZoomLevels\x3c/code\x3e affhange don. Dat dat \x3ci lang=\"en\"\x3eWFS\x3c/i\x3e-Nivvoh övverhoup de Eijeschaff „\x3ccode lang=\"en\"\x3eminZoomLevel\x3c/code\x3e“ pröhfe deiht, es noch övveresch vun fröhjer. Mer künne dat ävver jez nit fott lohße, oohne dat mer Jevaa loufe, dat Aanwendunge vun OpenLayers nit mieh loufe, di sesch doh velleijsch noch drop am verlohße sin. Dröm sare mer, dat mer et nit mieh han welle, un de „\x3ccode lang=\"en\"\x3eminZoomLevel\x3c/code\x3e“-Eijeschaff weed hee vun de Version 3.0 af nit mieh jeprööf wäde. Nemm doför de Enstellung för de hühßte un de kleinßte Oplöhsung, esu wi et en http://trac.openlayers.org/wiki/SettingZoomLevels opjeschrevve es.",'commitSuccess':"Dä \x3ci lang=\"en\"\x3eWFS\x3c/i\x3e-Vörjang es joot jeloufe: ${response}",'commitFailed':"Dä \x3ci lang=\"en\"\x3eWFS\x3c/i\x3e-Vörjang es scheif jejange: ${response}",'googleWarning':"Dat Nivvoh \x3ccode lang=\"en\"\x3eGoogle\x3c/code\x3e kunnt nit reschtesch jelaade wääde.\x3cbr /\x3e\x3cbr /\x3eÖm hee di Nohreesch loß ze krijje, donn en ander Jrund-Nivvoh ußsöhke, rähß bovve en de Äk.\x3cbr /\x3e\x3cbr /\x3eWascheinlesch es dat wiel dat \x3ci lang=\"en\"\x3eGoogle-Maps\x3c/i\x3e-Skrepp entweeder nit reschtesch enjebonge wood, udder nit dä reschtejje \x3ci lang=\"en\"\x3eAPI\x3c/i\x3e-Schlößel för Ding Web-ßait scheke deiht.\x3cbr /\x3e\x3cbr /\x3eFör Projrammierer jidd_et Hölp do_drövver, \x3ca href=\"http://trac.openlayers.org/wiki/Google\" target=\"_blank\"\x3ewi mer dat aan et Loufe brengk\x3c/a\x3e.",'getLayerWarning':"Dat Nivvoh \x3ccode\x3e${layerType}\x3c/code\x3e kunnt nit reschtesch jelaade wääde.\x3cbr /\x3e\x3cbr /\x3eÖm hee di Nohreesch loß ze krijje, donn en ander Jrund-Nivvoh ußsöhkre, rähß bovve en de Äk.\x3cbr /\x3e\x3cbr /\x3eWascheinlesch es dat, wiel dat Skrepp \x3ccode\x3e${layerLib}\x3c/code\x3e nit reschtesch enjebonge wood.\x3cbr /\x3e\x3cbr /\x3eFör Projrammierer jidd_Et Hölp do_drövver, \x3ca href=\"http://trac.openlayers.org/wiki/${layerLib}\" target=\"_blank\"\x3ewi mer dat aan et Loufe brengk\x3c/a\x3e.",'Scale = 1 : ${scaleDenom}':"Mohßshtaab = 1 : ${scaleDenom}",'W':"W",'E':"O",'N':"N",'S':"S",'reprojectDeprecated':"Do bruchs de Ußwahl \x3ccode\x3ereproject\x3c/code\x3e op däm Nivvoh \x3ccode\x3e${layerName}\x3c/code\x3e. Di Ußwahl es nit mieh jähn jesinn. Se wohr doför jedaach, öm Date op jeschääfsmäßesch eruß jejovve Kaate bovve drop ze moole, wat ävver enzwesche besser met dä Öngershtözung för de ßfääresche Mäkaator Beldscher jeiht. Doh kanns De mieh drövver fenge op dä Sigg: http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Hee di Metood es nim_mih aktoäll un et weed se en dä Version 3.0 nit mieh jävve. Nemm \x3ccode\x3e${newMethod}\x3c/code\x3e doföör."});OpenLayers.Lang["fur"]=OpenLayers.Util.applyDefaults({'Permalink':"Leam Permanent",'Overlays':"Livei parsore",'Base Layer':"Livel di base",'browserNotSupported':"Il to sgarfadôr nol supuarte la renderizazion vetoriâl. Al moment a son supuartâts:\n${renderers}",'Scale = 1 : ${scaleDenom}':"Scjale = 1 : ${scaleDenom}",'W':"O",'E':"E",'N':"N",'S':"S"});OpenLayers.Lang["ja"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"未処理の要求は ${statusText} を返します",'Permalink':"パーマリンク",'Overlays':"オーバーレイ",'Base Layer':"基底レイヤー",'noFID':"FID のない地物は更新できません。",'browserNotSupported':"あなたのブラウザはベクターグラフィックスの描写に対応していません。現時点で対応しているソフトウェアは以下のものです。\n${renderers}",'minZoomLevelError':"minZoomLevel プロパティは FixedZoomLevels を継承するレイヤーでの使用のみを想定しています。この minZoomLevel に対する WFS レイヤーの検査は歴史的なものです。しかしながら、この検査を除去するとそれに依存する 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 マップ用ライブラリのスクリプトが組み込まれていないか、あなたのサイトに対応する正しい 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':"あなたは「${layerName}」レイヤーで reproject オプションを使っています。このオプションは商用の基底地図上に情報を表示する目的で設計されましたが、現在ではその機能は Spherical Mercator サポートを利用して実現されており、このオプションの使用は非推奨です。追加の情報は http://trac.openlayers.org/wiki/SphericalMercator で入手できます。",'methodDeprecated':"このメソッドは廃止が予定されており、バージョン3.0で除去されます。代わりに ${newMethod} を使用してください。"});(function(){var oXMLHttpRequest=window.XMLHttpRequest;var bGecko=!!window.controllers,bIE=window.document.all&&!window.opera,bIE7=bIE&&window.navigator.userAgent.match(/MSIE 7.0/);function fXMLHttpRequest(){this._object=oXMLHttpRequest&&!bIE7?new oXMLHttpRequest:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[];};function cXMLHttpRequest(){return new fXMLHttpRequest;};cXMLHttpRequest.prototype=fXMLHttpRequest.prototype;if(bGecko&&oXMLHttpRequest.wrapped) +Math.pow(t0.clientY-t1.clientY,2));},getPinchData:function(evt){var distance=this.getDistance(evt.touches);var scale=distance/this.start.distance;return{distance:distance,delta:this.last.distance-distance,scale:scale};},CLASS_NAME:"OpenLayers.Handler.Pinch"});OpenLayers.Lang['lt']=OpenLayers.Util.applyDefaults({'unhandledRequest':"Neapdorota užklausa gražino ${statusText}",'Permalink':"Pastovi nuoroda",'Overlays':"Papildomi sluoksniai",'Base Layer':"Pagrindinis sluoksnis",'noFID':"Negaliu atnaujinti objekto, kuris neturi FID.",'browserNotSupported':"Jūsų naršyklė nemoka parodyti vektorių. Šiuo metu galima naudotis tokiais rodymo varikliais:\n{renderers}",'commitSuccess':"WFS Tranzakcija: PAVYKO ${response}",'commitFailed':"WFS Tranzakcija: ŽLUGO ${response}",'Scale = 1 : ${scaleDenom}':"Mastelis = 1 : ${scaleDenom}",'W':'V','E':'R','N':'Š','S':'P','Graticule':'Tinklelis','methodDeprecated':"Šis metodas yra pasenęs ir 3.0 versijoje bus pašalintas. "+"Prašome naudoti ${newMethod}.",'end':''});OpenLayers.Lang.ca={'unhandledRequest':"Resposta a petició no gestionada ${statusText}",'Permalink':"Enllaç permanent",'Overlays':"Capes addicionals",'Base Layer':"Capa Base",'noFID':"No es pot actualitzar un element per al que no existeix FID.",'browserNotSupported':"El seu navegador no suporta renderització vectorial. Els renderitzadors suportats actualment són:\n${renderers}",'minZoomLevelError':"La propietat minZoomLevel s'ha d'utilitzar només "+"amb les capes que tenen FixedZoomLevels. El fet que "+"una capa wfs comprovi minZoomLevel és una relíquia del "+"passat. No podem, però, eliminar-la sense trencar "+"les aplicacions d'OpenLayers que en puguin dependre. "+"Així doncs estem fent-la obsoleta -- la comprovació "+"minZoomLevel s'eliminarà a la versió 3.0. Feu servir "+"els paràmetres min/max resolution en substitució, tal com es descriu aquí: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"Transacció WFS: CORRECTA ${response}",'commitFailed':"Transacció WFS: HA FALLAT ${response}",'googleWarning':"La capa Google no s'ha pogut carregar correctament.

    "+"Per evitar aquest missatge, seleccioneu una nova Capa Base "+"al gestor de capes de la cantonada superior dreta.

    "+"Probablement això és degut a que l'script de la biblioteca de "+"Google Maps no ha estat inclòs a la vostra pàgina, o no "+"conté la clau de l'API correcta per a la vostra adreça.

    "+"Desenvolupadors: Per obtenir consells sobre com fer anar això, "+"féu clic aquí",'getLayerWarning':"Per evitar aquest missatge, seleccioneu una nova Capa Base "+"al gestor de capes de la cantonada superior dreta.

    "+"Probablement això és degut a que l'script de la biblioteca "+"${layerLib} "+"no ha estat inclòs a la vostra pàgina.

    "+"Desenvolupadors: Per obtenir consells sobre com fer anar això, "+"féu clic aquí",'Scale = 1 : ${scaleDenom}':"Escala = 1 : ${scaleDenom}",'W':'O','E':'E','N':'N','S':'S','Graticule':'Retícula','reprojectDeprecated':"Esteu fent servir l'opció 'reproject' a la capa "+"${layerName}. Aquesta opció és obsoleta: el seu ús fou concebut "+"per suportar la visualització de dades sobre mapes base comercials, "+"però ara aquesta funcionalitat s'hauria d'assolir mitjançant el suport "+"de la projecció Spherical Mercator. Més informació disponible a "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Aquest mètode és obsolet i s'eliminarà a la versió 3.0. "+"Si us plau feu servir em mètode alternatiu ${newMethod}.",'end':''};OpenLayers.Lang["id"]=OpenLayers.Util.applyDefaults({'unhandledRequest':"Permintaan yang tak tertangani menghasilkan ${statusText}",'Permalink':"Pranala permanen",'Overlays':"Hamparan",'Base Layer':"Lapisan Dasar",'noFID':"Tidak dapat memperbarui fitur yang tidak memiliki FID.",'browserNotSupported':"Peramban Anda tidak mendukung penggambaran vektor. Penggambar yang didukung saat ini adalah:\n${renderers}",'minZoomLevelError':"Properti minZoomLevel hanya ditujukan bekerja dengan lapisan FixedZoomLevels-descendent. Pengecekan minZoomLevel oleh lapisan wfs adalah peninggalan masa lalu. Kami tidak dapat menghapusnya tanpa kemungkinan merusak aplikasi berbasis OL yang mungkin bergantung padanya. Karenanya, kami menganggapnya tidak berlaku -- Cek minZoomLevel di bawah ini akan dihapus pada 3.0. Silakan gunakan penyetelan resolusi min/maks seperti dijabarkan di sini: http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaksi: BERHASIL ${respon}",'commitFailed':"WFS Transaksi: GAGAL ${respon}",'googleWarning':"Lapisan Google tidak dapat dimuat dengan benar.\x3cbr\x3e\x3cbr\x3eUntuk menghilangkan pesan ini, pilih suatu BaseLayer baru melalui penukar lapisan (layer switcher) di ujung kanan atas.\x3cbr\x3e\x3cbr\x3eKemungkinan besar ini karena pustaka skrip Google Maps tidak disertakan atau tidak mengandung kunci API yang tepat untuk situs Anda.\x3cbr\x3e\x3cbr\x3ePengembang: Untuk bantuan mengatasi masalah ini, \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3eklik di sini\x3c/a\x3e",'getLayerWarning':"Lapisan ${layerType} tidak dapat dimuat dengan benar.\x3cbr\x3e\x3cbr\x3eUntuk menghilangkan pesan ini, pilih suatu BaseLayer baru melalui penukar lapisan (layer switcher) di ujung kanan atas.\x3cbr\x3e\x3cbr\x3eKemungkinan besar ini karena pustaka skrip Google Maps tidak disertakan dengan benar.\x3cbr\x3e\x3cbr\x3ePengembang: Untuk bantuan mengatasi masalah ini, \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eklik di sini\x3c/a\x3e",'Scale = 1 : ${scaleDenom}':"Sekala = 1 : ${scaleDenom}",'W':"B",'E':"T",'N':"U",'S':"S",'reprojectDeprecated':"Anda menggunakan opsi \'reproject\' pada lapisan ${layerName}. Opsi ini telah ditinggalkan: penggunaannya dirancang untuk mendukung tampilan data melalui peta dasar komersial, tapi fungsionalitas tersebut saat ini harus dilakukan dengan menggunakan dukungan Spherical Mercator. Informasi lebih lanjut tersedia di http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"Metode ini telah usang dan akan dihapus di 3.0. Sebaliknya, harap gunakan ${newMethod}."});OpenLayers.Filter.Comparison=OpenLayers.Class(OpenLayers.Filter,{type:null,property:null,value:null,matchCase:true,lowerBoundary:null,upperBoundary:null,initialize:function(options){OpenLayers.Filter.prototype.initialize.apply(this,[options]);if(this.type===OpenLayers.Filter.Comparison.LIKE&&options.matchCase===undefined){this.matchCase=null;}},evaluate:function(context){if(context instanceof OpenLayers.Feature.Vector){context=context.attributes;} +var result=false;var got=context[this.property];var exp;switch(this.type){case OpenLayers.Filter.Comparison.EQUAL_TO:exp=this.value;if(!this.matchCase&&typeof got=="string"&&typeof exp=="string"){result=(got.toUpperCase()==exp.toUpperCase());}else{result=(got==exp);} +break;case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:exp=this.value;if(!this.matchCase&&typeof got=="string"&&typeof exp=="string"){result=(got.toUpperCase()!=exp.toUpperCase());}else{result=(got!=exp);} +break;case OpenLayers.Filter.Comparison.LESS_THAN:result=gotthis.value;break;case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:result=got<=this.value;break;case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:result=got>=this.value;break;case OpenLayers.Filter.Comparison.BETWEEN:result=(got>=this.lowerBoundary)&&(got<=this.upperBoundary);break;case OpenLayers.Filter.Comparison.LIKE:var regexp=new RegExp(this.value,"gi");result=regexp.test(got);break;} +return result;},value2regex:function(wildCard,singleChar,escapeChar){if(wildCard=="."){throw new Error("'.' is an unsupported wildCard character for "+"OpenLayers.Filter.Comparison");} +wildCard=wildCard?wildCard:"*";singleChar=singleChar?singleChar:".";escapeChar=escapeChar?escapeChar:"!";this.value=this.value.replace(new RegExp("\\"+escapeChar+"(.|$)","g"),"\\$1");this.value=this.value.replace(new RegExp("\\"+singleChar,"g"),".");this.value=this.value.replace(new RegExp("\\"+wildCard,"g"),".*");this.value=this.value.replace(new RegExp("\\\\.\\*","g"),"\\"+wildCard);this.value=this.value.replace(new RegExp("\\\\\\.","g"),"\\"+singleChar);return this.value;},regex2value:function(){var value=this.value;value=value.replace(/!/g,"!!");value=value.replace(/(\\)?\\\./g,function($0,$1){return $1?$0:"!.";});value=value.replace(/(\\)?\\\*/g,function($0,$1){return $1?$0:"!*";});value=value.replace(/\\\\/g,"\\");value=value.replace(/\.\*/g,"*");return value;},clone:function(){return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(),this);},CLASS_NAME:"OpenLayers.Filter.Comparison"});OpenLayers.Filter.Comparison.EQUAL_TO="==";OpenLayers.Filter.Comparison.NOT_EQUAL_TO="!=";OpenLayers.Filter.Comparison.LESS_THAN="<";OpenLayers.Filter.Comparison.GREATER_THAN=">";OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO="<=";OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO=">=";OpenLayers.Filter.Comparison.BETWEEN="..";OpenLayers.Filter.Comparison.LIKE="~";OpenLayers.Format.QueryStringFilter=(function(){var cmpToStr={};cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO]="eq";cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO]="ne";cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN]="lt";cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO]="lte";cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN]="gt";cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO]="gte";cmpToStr[OpenLayers.Filter.Comparison.LIKE]="ilike";function regex2value(value){value=value.replace(/%/g,"\\%");value=value.replace(/\\\\\.(\*)?/g,function($0,$1){return $1?$0:"\\\\_";});value=value.replace(/\\\\\.\*/g,"\\\\%");value=value.replace(/(\\)?\.(\*)?/g,function($0,$1,$2){return $1||$2?$0:"_";});value=value.replace(/(\\)?\.\*/g,function($0,$1){return $1?$0:"%";});value=value.replace(/\\\./g,".");value=value.replace(/(\\)?\\\*/g,function($0,$1){return $1?$0:"*";});return value;} +return OpenLayers.Class(OpenLayers.Format,{wildcarded:false,srsInBBOX:false,write:function(filter,params){params=params||{};var className=filter.CLASS_NAME;var filterType=className.substring(className.lastIndexOf(".")+1);switch(filterType){case"Spatial":switch(filter.type){case OpenLayers.Filter.Spatial.BBOX:params.bbox=filter.value.toArray();if(this.srsInBBOX&&filter.projection){params.bbox.push(filter.projection.getCode());} +break;case OpenLayers.Filter.Spatial.DWITHIN:params.tolerance=filter.distance;case OpenLayers.Filter.Spatial.WITHIN:params.lon=filter.value.x;params.lat=filter.value.y;break;default:OpenLayers.Console.warn("Unknown spatial filter type "+filter.type);} +break;case"Comparison":var op=cmpToStr[filter.type];if(op!==undefined){var value=filter.value;if(filter.type==OpenLayers.Filter.Comparison.LIKE){value=regex2value(value);if(this.wildcarded){value="%"+value+"%";}} +params[filter.property+"__"+op]=value;params.queryable=params.queryable||[];params.queryable.push(filter.property);}else{OpenLayers.Console.warn("Unknown comparison filter type "+filter.type);} +break;case"Logical":if(filter.type===OpenLayers.Filter.Logical.AND){for(var i=0,len=filter.filters.length;i0){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