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("ajouter un emplacement");
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("Vous pouvez déplacer le marqueur en effectuant un glisser-déposer.");
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("Pour modifier les données d'une image, sélectionnez le marqueur correspondant.");
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("Cliquez sur la carte pour ajouter un marqueur.");
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)
369 save: function (feature) {
370 var x = feature.geometry.x;
371 var y = feature.geometry.y;
373 var mapProj = feature.layer.map.getProjectionObject();
374 var lonlat = new OpenLayers.LonLat(x, y).
376 new OpenLayers.Projection("EPSG:4326"));
377 var form = $("#feature_update");
378 form.find('input[name="lon"]').val(lonlat.lon);
379 form.find('input[name="lat"]').val(lonlat.lat);
380 form.find('input[name="fid"]').val(feature.fid);
381 form.find('input[name="keep_img"]').val(
382 $("#img").attr("src") ? "yes": "no"
386 form.find('input[name="request"]').val("update");
388 form.find('input[name="request"]').val("add");
392 oncomplete: OpenLayers.Function.bind(this.ajaxReply, this)
396 ajaxReply: function (data) {
398 this.commError("Il s'est produit une erreur serveur.");
402 var xml = new OpenLayers.Format.XML().read(data);
404 switch (xml.documentElement.nodeName.toLowerCase()) {
406 switch (xml.documentElement.getAttribute("reason")) {
408 $("#login_area").show();
413 this.commError("Il s'est produit une erreur serveur.");
417 this.commError("La fiche n'était pas référencée sur le serveur.");
418 Admin.reloadLayer(Admin.dataLayer);
422 this.commError("Aucun changement n'a été effectué.");
426 this.commError("Le serveur n'a pas compris la requête. Il s'agit probablement d'un bug dans SYP.");
430 this.commError("L'image est trop grande et n'a pas été acceptée par le serveur.");
431 $("#image_file").parent().html($("#image_file").parent().html());
432 $("#image_file").focus();
435 this.commError("Le fichier ne semble pas être une image.");
436 $("#image_file").parent().html($("#image_file").parent().html());
437 $("#image_file").focus();
440 this.commError("Il s'est produit une erreur inconnue.");
446 switch (xml.documentElement.getAttribute("request")) {
448 this.commSuccess("La suppression s'est déroulée correctement.");
449 var someFeature = false;
451 $.each($(xml).find("FEATURE,feature"), function () {
453 var id = parseFloat($(this).find("ID:first,id:first").text());
454 if ((id === null) || isNaN (id)) {
457 var features = Admin.dataLayer.features;
458 for (var idx = 0; idx < features.length; idx++) {
459 if (features[idx].fid == id) {
460 Admin.dataLayer.removeFeatures([features[idx]]);
464 if (someFeature == false) {
465 this.commError("Le serveur a fait une réponse incohérente.");
472 var someFeature = false;
474 $.each($(xml).find("FEATURE,feature"), function () {
476 var id = parseFloat($(this).find("ID:first,id:first").text());
477 if ((id === null) || isNaN (id)) {
481 var lon = parseFloat($(this).find("LON:first,lon:first").text());
482 if ((typeof (lon) != "number") || isNaN (lon) ||
483 (lon < -180) || (lon > 180)) {
487 var lat = parseFloat($(this).find("LAT:first,lat:first").text());
488 if ((typeof (lat) != "number") || isNaN (lat) ||
489 (lat < -90) || (lat > 90)) {
493 var mapProj = Admin.map.getProjectionObject();
494 var lonlat = new OpenLayers.LonLat (lon, lat).
495 transform( new OpenLayers.Projection("EPSG:4326"), mapProj);
497 var imgurl = $(this).find("IMGURL:first,imgurl:first").text();
498 var title = $(this).find("HEADING:first,heading:first").text();
499 var description = $(this).find("DESCRIPTION:first,description:first").text();
501 feature = self.update (Admin.currentFeature, lonlat, imgurl, title, description);
505 if (someFeature == false) {
506 this.commError("Le serveur a fait une réponse incohérente.");
508 this.commSuccess("La sauvegarde s'est déroulée correctement.");
514 this.commError("Le serveur a fait une réponse incohérente.");
519 this.commError("Le serveur a fait une réponse incohérente.");
524 commSuccess: function (message) {
525 $("#server_comm").text(message);
526 $("#server_comm").removeClass().addClass("success");
529 commError: function (message) {
530 $("#server_comm").text(message);
531 $("#server_comm").removeClass().addClass("error");
532 if (message.length) {
533 // this.move(Admin.currentFeature, Admin.currentFeatureLocation);
539 /* maintains a queue of ajax queries, so I'm sure they all execute in the same
540 * order they were defined */
546 add: function(query) {
547 this._queue.push(query);
548 if (this._queue.length > 1) {
551 this._runQuery(query);
555 _runQuery: function(query) {
557 $('#api_frame').one("load", function() {
558 self.running = false;
560 if (typeof (query.oncomplete) == "function") {
563 if (this.contentDocument) {
564 body = this.contentDocument.body;
565 } else if (this.contentWindow) {
566 body = this.contentWindow.document.body;
568 body = document.frames[this.id].document.body;
572 query.oncomplete(body.innerHTML);
574 query.oncomplete(null);
578 query.form.attr("action", "api.php");
579 query.form.attr("target", "api_frame");
580 query.form.attr("method", "post");
582 query.form.get(0).submit();
583 if (typeof (query.onsend) == "function") {
588 _reqEnd: function() {
590 if (this._queue.length > 0) {
591 this._reqEnd(this._queue[0]);
599 $("#login_form").submit(this.submit);
600 $("#password").focus().select();
607 submit: function () {
609 pwdMgr.commError("");
611 form: $("#login_form"),
613 $("#pwd_throbber").css("visibility", "visible");
614 $("#login_error").hide();
616 // we need a timeout; otherwise those fields will not be submitted
617 window.setTimeout(function() {
618 // removes focus from #password before disabling it. Otherwise, opera
619 // prevents re-focusing it after re-enabling it.
620 $("#password").blur();
621 $("#login_submit, #password").attr("disabled", "disabled");
624 oncomplete: OpenLayers.Function.bind(pwdMgr.ajaxReply, pwdMgr)
631 ajaxReply: function (data) {
633 $("#pwd_throbber").css("visibility", "hidden");
634 // here, we need a timeout because onsend timeout sometimes has not been triggered yet
635 window.setTimeout(function() {
636 $("#login_submit, #password").removeAttr("disabled");
640 this.commError("Il s'est produit une erreur serveur.");
641 $("#login_error").show();
642 window.setTimeout(function() {
643 $("#password").focus().select();
647 var xml = new OpenLayers.Format.XML().read(data);
649 switch (xml.documentElement.nodeName.toLowerCase()) {
651 switch (xml.documentElement.getAttribute("reason")) {
653 this.commError("Il s'est produit une erreur serveur.");
656 this.commError("Le mot de passe n'est pas correct.");
659 this.commError("Le serveur n'a pas compris la requête. Il s'agit probablement d'un bug dans SYP.");
662 this.commError("Il s'est produit une erreur inconnue.");
665 $("#login_error").show();
666 window.setTimeout(function() {
667 $("#password").focus().select();
672 $("#login_area").hide();
675 this.commError("Le serveur a fait une réponse incohérente.");
680 commError: function (message) {
681 $("#login_error").text(message);
683 $("#login_error").show();
685 $("#login_error").hide();
690 $(window).load(function () {
691 // if using .ready, ie triggers an error when trying to access
692 // document.namespaces
694 $("#newfeature_button").click(function () {
695 Admin.addNewFeature();
697 $("#editor_close").click(function () {
698 Admin.cancelCurrentFeature()
700 $("#feature_update").submit(function() {
702 FeatureMgr.save(Admin.currentFeature);
706 $("#feature_delete").submit(function() {
708 FeatureMgr.del(Admin.currentFeature);
712 $("#image_delete").click(function() {
713 $("#img").removeAttr('src');
714 // needs to rebuild element otherwise some browsers still
716 $("#img").parent().html($("#img").parent().html());
717 $("#img").parent().hide();
718 $("#image_delete").hide();
719 $("#image_file").parent().show();