1 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
4 // drag feature with tolerance
5 OpenLayers.Control.SypDragFeature = OpenLayers.Class (OpenLayers.Control.DragFeature, {
11 downFeature: function(pixel) {
12 OpenLayers.Control.DragFeature.prototype.downFeature.apply(this, arguments);
13 this.dragStart = (new Date()).getTime();
14 this.startPixel = pixel;
17 doneDragging: function(pixel) {
18 OpenLayers.Control.DragFeature.prototype.doneDragging.apply(this, arguments);
20 var passesTimeTolerance =
21 (new Date()).getTime() > this.dragStart + this.timeTolerance;
23 var xDiff = this.startPixel.x - pixel.x;
24 var yDiff = this.startPixel.y - pixel.y;
26 var passesPixelTolerance =
27 Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2)) > this.pixelTolerance;
29 if(passesTimeTolerance && passesPixelTolerance){
30 this.onComplete(this.feature, pixel);
32 var feature = this.feature;
33 var res = this.map.getResolution();
34 this.feature.geometry.move(res * (this.startPixel.x - this.lastPixel.x),
35 res * (this.lastPixel.y - this.startPixel.y));
36 this.layer.drawFeature(this.feature);
38 this.layer.drawFeature(this.feature, "select");
41 moveFeature: function(pixel) {
42 OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments);
43 this.layer.drawFeature(this.feature, "temporary");
46 overFeature: function (feature) {
47 // can only drag and drop currently selected feature
48 if (feature != Admin.currentFeature) {
51 OpenLayers.Control.DragFeature.prototype.overFeature.apply(this, arguments);
54 CLASS_NAME: "OpenLayers.Control.SypDragFeature"
59 MARKER_ICON: "openlayers/img/marker-blue.png",
60 MARKER_ICON_HEIGHT: 25,
61 MARKER_SELECT_ICON: "openlayers/img/marker-green.png",
62 MARKER_SELECT_ICON_HEIGHT: 25,
63 MARKER_TEMPORARY_ICON: "openlayers/img/marker-gold.png",
64 MARKER_TEMPORARY_ICON_HEIGHT: 25
70 selFeatureControl: null,
71 moveFeatureControl: null,
72 addFeatureControl: null,
75 currentFeatureLocation: null,
78 this.map = new OpenLayers.Map ("map", {
80 new OpenLayers.Control.Navigation (),
81 new OpenLayers.Control.PanZoom ()
83 projection: new OpenLayers.Projection("EPSG:900913"),
84 displayProjection: new OpenLayers.Projection("EPSG:4326")
87 this.baseLayer = this.createBaseLayer ();
88 this.dataLayer = this.createDataLayer ();
89 this.map.addLayers([this.baseLayer, this.dataLayer]);
92 this.selFeatureControl = this.createSelectFeatureControl();
93 this.map.addControl(this.selFeatureControl);
94 this.moveFeatureControl = this.createMoveFeatureControl();
95 this.map.addControl(this.moveFeatureControl);
96 this.addFeatureControl = this.createNewfeatureControl();
97 this.map.addControl(this.addFeatureControl);
100 var centerBounds = new OpenLayers.Bounds();
102 var mapProj = this.map.getProjectionObject();
103 var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
105 var bottomLeft = new OpenLayers.LonLat(sypOrig[0],sypOrig[1]);
106 bottomLeft = bottomLeft.transform(sypOrigProj, mapProj);
107 var topRight = new OpenLayers.LonLat(sypOrig[2],sypOrig[3])
108 topRight = topRight.transform(sypOrigProj, mapProj);
110 centerBounds.extend(bottomLeft);
111 centerBounds.extend(topRight);
113 // at that moment, ie does not know size of the map, we need to update
115 this.map.updateSize();
116 this.map.zoomToExtent(centerBounds);
122 this.addFeatureControl.deactivate();
123 this.moveFeatureControl.deactivate();
124 this.selFeatureControl.activate();
125 this.checkForFeatures();
126 $("#newfeature_button").show().val(SypStrings.AddItem);
127 $("#newfeature_button").unbind("click").click(function () {
128 Admin.addNewFeature();
132 createBaseLayer: function () {
133 return new OpenLayers.Layer.OSM("OSM");
136 createDataLayer: function () {
137 var styleMap = new OpenLayers.StyleMap (
139 externalGraphic: this.Settings.MARKER_ICON,
140 graphicHeight: this.Settings.MARKER_ICON_HEIGHT
144 externalGraphic: this.Settings.MARKER_TEMPORARY_ICON,
145 graphicHeight: this.Settings.MARKER_TEMPORARY_ICON_HEIGHT
149 externalGraphic: this.Settings.MARKER_SELECT_ICON,
150 graphicHeight: this.Settings.MARKER_SELECT_ICON_HEIGHT
154 var layer = new OpenLayers.Layer.GML("KML", "items.php",
157 format: OpenLayers.Format.KML,
158 projection: this.map.displayProjection,
159 eventListeners: { scope: this,
160 loadend: this.checkForFeatures
167 createMoveFeatureControl: function () {
168 var control = new OpenLayers.Control.SypDragFeature(
174 createSelectFeatureControl: function () {
175 var control = new OpenLayers.Control.SelectFeature(
177 onSelect: OpenLayers.Function.bind(this.onFeatureSelect, this)
182 createNewfeatureControl: function () {
183 var control = new OpenLayers.Control ();
184 var handler = new OpenLayers.Handler.Click(control, {
185 'click': OpenLayers.Function.bind(FeatureMgr.add, FeatureMgr)
187 control.handler = handler;
191 onFeatureSelect: function (feature) {
192 this.showEditor(feature);
194 this.selFeatureControl.deactivate();
195 this.moveFeatureControl.activate();
198 closeEditor: function() {
199 if (this.currentFeature && this.currentFeature.layer) {
200 this.selFeatureControl.unselect(this.currentFeature);
202 this.currentFeature = null;
203 this.currentFeatureLocation = null;
204 $("#img").removeAttr('src');
205 $("#img").parent().html($("#img").parent().html());
206 $("#img").parent().show();
207 $("#title, #description").val("");
209 // do it once before hidding and once after hidding to work in all cases
210 $("#title, #description").val("");
211 $("#image_file").parent().html($("#image_file").parent().html());
212 $(document).unbind("keydown");
213 this.checkForFeatures();
217 showEditor: function (feature) {
218 $("#newfeature_button").hide();
224 $(document).unbind("keydown").keydown(function(e) {
225 if (e.keyCode == 27) {
226 Admin.cancelCurrentFeature()
230 this.currentFeature = feature;
231 this.currentFeatureLocation = new OpenLayers.Pixel(feature.geometry.x, feature.geometry.y);
233 $("#instructions").text(SypStrings.DragDropHowto);
234 $("#title").val(feature.attributes.name);
235 var fullDesc = $(feature.attributes.description).parent();
236 $("#description").val(fullDesc.find('p').text());
237 var src = fullDesc.find('img').attr('src');
239 $("#img").parent().show();
240 $("#img").attr('src', src);
241 $("#image_file").parent().hide();
242 $("#image_delete").show();
244 $("#img").parent().hide();
245 $("#image_file").parent().show();
246 $("#image_delete").hide();
248 $("#title").select().focus();
251 checkForFeatures: function () {
252 if (this.dataLayer.features.length != 0) {
253 $("#instructions").text(SypStrings.SelectHowto);
257 addNewFeature: function () {
259 $(document).unbind("keydown");
262 $(document).unbind("keydown").keydown(function(e) {
263 if (e.keyCode == 27) {
269 $("#newfeature_button").val("annuler");
270 $("#newfeature_button").unbind("click").click(cancel);
272 $("#instructions").text(SypStrings.AddHowto);
273 this.selFeatureControl.deactivate();
274 this.addFeatureControl.activate();
278 cancelCurrentFeature: function() {
279 if (AjaxMgr.running) {
282 var feature = this.currentFeature;
284 FeatureMgr.move (feature, this.currentFeatureLocation);
286 this.dataLayer.removeFeatures([feature]);
291 reloadLayer: function (layer) {
292 layer.destroyFeatures();
293 layer.loaded = false;
298 escapeHTML: function (str) {
303 replace(/&/gm, '&').
304 replace(/'/gm, ''').
305 replace(/"/gm, '"').
306 replace(/>/gm, '>').
307 replace(/</gm, '<');
310 startsWith: function (str, prefix) {
311 return (str.slice(0, prefix.length) == prefix);
314 indexOf: function (array, item) {
315 if (array.indexOf !== undefined) {
316 return array.indexOf(item);
318 return OpenLayers.Util.indexOf(array, item);
331 var pos = map.getLonLatFromViewPortPx(evt.xy);
332 feature = this.update (null, pos, "", "", "");
333 Admin.addFeatureControl.deactivate();
334 Admin.selFeatureControl.select(feature);
337 move: function (feature, aLocation) {
338 if (!feature || !aLocation) {
341 var curLoc = feature.geometry;
342 feature.geometry.move(aLocation.x - curLoc.x, aLocation.y - curLoc.y);
343 feature.layer.drawFeature(feature);
346 update: function(feature, lonlat, imgurl, title, description) {
347 var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
349 feature = new OpenLayers.Feature.Vector(point);
350 Admin.dataLayer.addFeatures([feature]);
352 this.move (feature, point);
354 feature.attributes.name = title;
355 feature.attributes.description = "<p>" + Admin.Utils.escapeHTML(description) + "</p>"
356 + "<img src=\"" + imgurl + "\">"
360 del: function (feature) {
361 var form = $("#feature_delete");
362 form.find('input[name="fid"]').val(feature.fid);
365 oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
366 onsend: function() { $("#editor_throbber").css("visibility", "visible"); }
370 save: function (feature) {
371 var x = feature.geometry.x;
372 var y = feature.geometry.y;
374 var mapProj = feature.layer.map.getProjectionObject();
375 var lonlat = new OpenLayers.LonLat(x, y).
377 new OpenLayers.Projection("EPSG:4326"));
378 var form = $("#feature_update");
379 form.find('input[name="lon"]').val(lonlat.lon);
380 form.find('input[name="lat"]').val(lonlat.lat);
381 form.find('input[name="fid"]').val(feature.fid);
382 form.find('input[name="keep_img"]').val(
383 $("#img").attr("src") ? "yes": "no"
387 form.find('input[name="request"]').val("update");
389 form.find('input[name="request"]').val("add");
393 oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
394 onsend: function() { $("#editor_throbber").css("visibility", "visible"); }
398 ajaxReply: function (data) {
399 $("#editor_throbber").css("visibility", "hidden");
401 this.commError(SypStrings.ServerError);
405 var xml = new OpenLayers.Format.XML().read(data);
407 switch (xml.documentElement.nodeName.toLowerCase()) {
409 switch (xml.documentElement.getAttribute("reason")) {
411 $("#login_area").show();
412 $("#cookie_warning").show();
414 Admin.cancelCurrentFeature();
418 this.commError(SypStrings.ServerError);
422 this.commError(SypStrings.UnreferencedError);
423 Admin.reloadLayer(Admin.dataLayer);
427 this.commError(SypStrings.NochangeError);
431 this.commError(SypStrings.RequestError);
435 this.commError(SypStrings.ToobigError);
436 $("#image_file").parent().html($("#image_file").parent().html());
437 $("#image_file").focus();
440 this.commError(SypStrings.NotimageError);
441 $("#image_file").parent().html($("#image_file").parent().html());
442 $("#image_file").focus();
445 this.commError(SypStrings.UnknownError);
451 switch (xml.documentElement.getAttribute("request")) {
453 this.commSuccess(SypStrings.DelSucces);
454 var someFeature = false;
456 $.each($(xml).find("FEATURE,feature"), function () {
458 var id = parseFloat($(this).find("ID:first,id:first").text());
459 if ((id === null) || isNaN (id)) {
462 var features = Admin.dataLayer.features;
463 for (var idx = 0; idx < features.length; idx++) {
464 if (features[idx].fid == id) {
465 Admin.dataLayer.removeFeatures([features[idx]]);
469 if (someFeature == false) {
470 this.commError(SypStrings.UnconsistentError);
477 var someFeature = false;
479 $.each($(xml).find("FEATURE,feature"), function () {
481 var id = parseFloat($(this).find("ID:first,id:first").text());
482 if ((id === null) || isNaN (id)) {
486 var lon = parseFloat($(this).find("LON:first,lon:first").text());
487 if ((typeof (lon) != "number") || isNaN (lon) ||
488 (lon < -180) || (lon > 180)) {
492 var lat = parseFloat($(this).find("LAT:first,lat:first").text());
493 if ((typeof (lat) != "number") || isNaN (lat) ||
494 (lat < -90) || (lat > 90)) {
498 var mapProj = Admin.map.getProjectionObject();
499 var lonlat = new OpenLayers.LonLat (lon, lat).
500 transform( new OpenLayers.Projection("EPSG:4326"), mapProj);
502 var imgurl = $(this).find("IMGURL:first,imgurl:first").text();
503 var title = $(this).find("HEADING:first,heading:first").text();
504 var description = $(this).find("DESCRIPTION:first,description:first").text();
506 feature = self.update (Admin.currentFeature, lonlat, imgurl, title, description);
510 if (someFeature == false) {
511 this.commError(SypStrings.UnconsistentError);
513 this.commSuccess(SypStrings.UpdateSucces);
519 this.commError(SypStrings.UnconsistentError);
524 this.commError(SypStrings.UnconsistentError);
529 commSuccess: function (message) {
530 $("#server_comm").text(message);
531 $("#server_comm").removeClass().addClass("success");
534 commError: function (message) {
535 $("#server_comm").text(message);
536 $("#server_comm").removeClass().addClass("error");
540 /* maintains a queue of ajax queries, so I'm sure they all execute in the same
541 * order they were defined */
547 add: function(query) {
548 this._queue.push(query);
549 if (this._queue.length > 1) {
552 this._runQuery(query);
556 _runQuery: function(query) {
558 $('#api_frame').one("load", function() {
559 self.running = false;
561 if (typeof (query.oncomplete) == "function") {
564 if (this.contentDocument) {
565 body = this.contentDocument.body;
566 } else if (this.contentWindow) {
567 body = this.contentWindow.document.body;
569 body = document.frames[this.id].document.body;
573 query.oncomplete(body.innerHTML);
575 query.oncomplete(null);
579 query.form.attr("action", "api.php");
580 query.form.attr("target", "api_frame");
581 query.form.attr("method", "post");
583 query.form.get(0).submit();
584 if (typeof (query.onsend) == "function") {
589 _reqEnd: function() {
591 if (this._queue.length > 0) {
592 this._reqEnd(this._queue[0]);
600 $("#login_form").submit(this.submit);
601 $("#password").focus().select();
608 submit: function () {
610 pwdMgr.commError("");
612 form: $("#login_form"),
614 $("#pwd_throbber").css("visibility", "visible");
615 $("#login_error").hide();
617 // we need a timeout; otherwise those fields will not be submitted
618 window.setTimeout(function() {
619 // removes focus from #password before disabling it. Otherwise, opera
620 // prevents re-focusing it after re-enabling it.
621 $("#password").blur();
622 $("#login_submit, #password").attr("disabled", "disabled");
625 oncomplete: OpenLayers.Function.bind(pwdMgr.ajaxReply, pwdMgr)
632 ajaxReply: function (data) {
634 $("#pwd_throbber").css("visibility", "hidden");
635 // here, we need a timeout because onsend timeout sometimes has not been triggered yet
636 window.setTimeout(function() {
637 $("#login_submit, #password").removeAttr("disabled");
641 this.commError(SypStrings.ServerError);
642 $("#login_error").show();
643 window.setTimeout(function() {
644 $("#password").focus().select();
648 var xml = new OpenLayers.Format.XML().read(data);
650 switch (xml.documentElement.nodeName.toLowerCase()) {
652 switch (xml.documentElement.getAttribute("reason")) {
654 this.commError(SypStrings.ServerError);
657 this.commError(SypStrings.UnauthorizedError);
660 this.commError(SypStrings.RequestError);
663 this.commError(SypStrings.UnknownError);
666 $("#login_error").show();
667 window.setTimeout(function() {
668 $("#password").focus().select();
673 $("#login_area").hide();
676 this.commError(SypStrings.UnconsistentError);
681 commError: function (message) {
682 $("#login_error").text(message);
684 $("#login_error").show();
686 $("#login_error").hide();
691 $(window).load(function () {
692 // if using .ready, ie triggers an error when trying to access
693 // document.namespaces
695 $("#newfeature_button").click(function () {
696 Admin.addNewFeature();
698 $("#editor_close").click(function () {
699 Admin.cancelCurrentFeature()
701 $("#feature_update").submit(function() {
703 FeatureMgr.save(Admin.currentFeature);
707 $("#feature_delete").submit(function() {
709 FeatureMgr.del(Admin.currentFeature);
713 $("#image_delete").click(function() {
714 $("#img").removeAttr('src');
715 // needs to rebuild element otherwise some browsers still
717 $("#img").parent().html($("#img").parent().html());
718 $("#img").parent().hide();
719 $("#image_delete").hide();
720 $("#image_file").parent().show();