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: "media/marker-normal.png",
60 MARKER_SELECT_ICON: "media/marker-selected.png",
61 MARKER_TEMPORARY_ICON: "media/marker-temp.png",
68 selFeatureControl: null,
69 moveFeatureControl: null,
70 addFeatureControl: null,
73 currentFeatureLocation: null,
76 this.map = new OpenLayers.Map ("map", {
78 new OpenLayers.Control.Navigation (),
79 new OpenLayers.Control.PanZoom ()
81 projection: new OpenLayers.Projection("EPSG:900913"),
82 displayProjection: new OpenLayers.Projection("EPSG:4326")
85 this.baseLayer = this.createBaseLayer ();
86 this.dataLayer = this.createDataLayer ();
87 this.map.addLayers([this.baseLayer, this.dataLayer]);
90 this.selFeatureControl = this.createSelectFeatureControl();
91 this.map.addControl(this.selFeatureControl);
92 this.moveFeatureControl = this.createMoveFeatureControl();
93 this.map.addControl(this.moveFeatureControl);
94 this.addFeatureControl = this.createNewfeatureControl();
95 this.map.addControl(this.addFeatureControl);
98 var centerBounds = new OpenLayers.Bounds();
100 var mapProj = this.map.getProjectionObject();
101 var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
103 var bottomLeft = new OpenLayers.LonLat(sypOrig[0],sypOrig[1]);
104 bottomLeft = bottomLeft.transform(sypOrigProj, mapProj);
105 var topRight = new OpenLayers.LonLat(sypOrig[2],sypOrig[3])
106 topRight = topRight.transform(sypOrigProj, mapProj);
108 centerBounds.extend(bottomLeft);
109 centerBounds.extend(topRight);
111 // at that moment, ie does not know size of the map, we need to update
113 this.map.updateSize();
114 this.map.zoomToExtent(centerBounds);
120 this.addFeatureControl.deactivate();
121 this.moveFeatureControl.deactivate();
122 this.selFeatureControl.activate();
123 this.checkForFeatures();
124 $("#newfeature_button").show().val(SypStrings.AddItem);
125 $("#newfeature_button").unbind("click").click(function () {
126 Admin.addNewFeature();
130 createBaseLayer: function () {
131 return new OpenLayers.Layer.OSM("OSM");
134 createDataLayer: function () {
135 var styleMap = new OpenLayers.StyleMap (
137 externalGraphic: this.Settings.MARKER_ICON,
138 graphicHeight: this.Settings.MARKER_HEIGHT || 32
141 externalGraphic: this.Settings.MARKER_TEMPORARY_ICON,
142 graphicHeight: this.Settings.MARKER_HEIGHT || 32
145 externalGraphic: this.Settings.MARKER_SELECT_ICON,
146 graphicHeight: this.Settings.MARKER_HEIGHT || 32
149 var layer = new OpenLayers.Layer.GML("KML", "items.php",
152 format: OpenLayers.Format.KML,
153 projection: this.map.displayProjection,
154 eventListeners: { scope: this,
155 loadend: this.checkForFeatures
162 createMoveFeatureControl: function () {
163 var control = new OpenLayers.Control.SypDragFeature(
169 createSelectFeatureControl: function () {
170 var control = new OpenLayers.Control.SelectFeature(
172 onSelect: OpenLayers.Function.bind(this.onFeatureSelect, this)
177 createNewfeatureControl: function () {
178 var control = new OpenLayers.Control ();
179 var handler = new OpenLayers.Handler.Click(control, {
180 'click': OpenLayers.Function.bind(FeatureMgr.add, FeatureMgr)
182 control.handler = handler;
186 onFeatureSelect: function (feature) {
187 this.showEditor(feature);
189 this.selFeatureControl.deactivate();
190 this.moveFeatureControl.activate();
193 closeEditor: function() {
194 if (this.currentFeature && this.currentFeature.layer) {
195 this.selFeatureControl.unselect(this.currentFeature);
197 this.currentFeature = null;
198 this.currentFeatureLocation = null;
199 $("#img").removeAttr('src');
200 $("#img").parent().html($("#img").parent().html());
201 $("#img").parent().show();
202 $("#title, #description").val("");
204 // do it once before hidding and once after hidding to work in all cases
205 $("#title, #description").val("");
206 $("#image_file").parent().html($("#image_file").parent().html());
207 $(document).unbind("keydown");
208 this.checkForFeatures();
212 showEditor: function (feature) {
213 $("#newfeature_button").hide();
219 $(document).unbind("keydown").keydown(function(e) {
220 if (e.keyCode == 27) {
221 Admin.cancelCurrentFeature()
225 this.currentFeature = feature;
226 this.currentFeatureLocation = new OpenLayers.Pixel(feature.geometry.x, feature.geometry.y);
228 $("#instructions").text(SypStrings.DragDropHowto);
229 $("#title").val(feature.attributes.name);
230 var fullDesc = $(feature.attributes.description).parent();
231 $("#description").val(fullDesc.find('p').text());
232 var src = fullDesc.find('img').attr('src');
234 $("#img").parent().show();
235 $("#img").attr('src', src);
236 $("#image_file").parent().hide();
237 $("#image_delete").show();
239 $("#img").parent().hide();
240 $("#image_file").parent().show();
241 $("#image_delete").hide();
243 $("#title").select().focus();
246 checkForFeatures: function () {
247 if (this.dataLayer.features.length != 0) {
248 $("#instructions").text(SypStrings.SelectHowto);
252 addNewFeature: function () {
254 $(document).unbind("keydown");
257 $(document).unbind("keydown").keydown(function(e) {
258 if (e.keyCode == 27) {
264 $("#newfeature_button").val("annuler");
265 $("#newfeature_button").unbind("click").click(cancel);
267 $("#instructions").text(SypStrings.AddHowto);
268 this.selFeatureControl.deactivate();
269 this.addFeatureControl.activate();
273 cancelCurrentFeature: function() {
274 if (AjaxMgr.running) {
277 var feature = this.currentFeature;
279 FeatureMgr.move (feature, this.currentFeatureLocation);
281 this.dataLayer.removeFeatures([feature]);
286 reloadLayer: function (layer) {
287 layer.destroyFeatures();
288 layer.loaded = false;
293 escapeHTML: function (str) {
298 replace(/&/gm, '&').
299 replace(/'/gm, ''').
300 replace(/"/gm, '"').
301 replace(/>/gm, '>').
302 replace(/</gm, '<');
305 startsWith: function (str, prefix) {
306 return (str.slice(0, prefix.length) == prefix);
309 indexOf: function (array, item) {
310 if (array.indexOf !== undefined) {
311 return array.indexOf(item);
313 return OpenLayers.Util.indexOf(array, item);
326 var pos = map.getLonLatFromViewPortPx(evt.xy);
327 feature = this.update (null, pos, "", "", "");
328 Admin.addFeatureControl.deactivate();
329 Admin.selFeatureControl.select(feature);
332 move: function (feature, aLocation) {
333 if (!feature || !aLocation) {
336 var curLoc = feature.geometry;
337 feature.geometry.move(aLocation.x - curLoc.x, aLocation.y - curLoc.y);
338 feature.layer.drawFeature(feature);
341 update: function(feature, lonlat, imgurl, title, description) {
342 var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
344 feature = new OpenLayers.Feature.Vector(point);
345 Admin.dataLayer.addFeatures([feature]);
347 this.move (feature, point);
349 feature.attributes.name = title;
350 feature.attributes.description = "<p>" + Admin.Utils.escapeHTML(description) + "</p>"
351 + "<img src=\"" + imgurl + "\">"
355 del: function (feature) {
356 var form = $("#feature_delete");
357 form.find('input[name="fid"]').val(feature.fid);
360 oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
361 onsend: function() { $("#editor_throbber").css("visibility", "visible"); }
365 save: function (feature) {
366 var x = feature.geometry.x;
367 var y = feature.geometry.y;
369 var mapProj = feature.layer.map.getProjectionObject();
370 var lonlat = new OpenLayers.LonLat(x, y).
372 new OpenLayers.Projection("EPSG:4326"));
373 var form = $("#feature_update");
374 form.find('input[name="lon"]').val(lonlat.lon);
375 form.find('input[name="lat"]').val(lonlat.lat);
376 form.find('input[name="fid"]').val(feature.fid);
377 form.find('input[name="keep_img"]').val(
378 $("#img").attr("src") ? "yes": "no"
382 form.find('input[name="request"]').val("update");
384 form.find('input[name="request"]').val("add");
388 oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
389 onsend: function() { $("#editor_throbber").css("visibility", "visible"); }
393 ajaxReply: function (data) {
394 $("#editor_throbber").css("visibility", "hidden");
396 this.commError(SypStrings.ServerError);
400 var xml = new OpenLayers.Format.XML().read(data);
402 switch (xml.documentElement.nodeName.toLowerCase()) {
404 switch (xml.documentElement.getAttribute("reason")) {
406 $("#login_area").show();
407 $("#cookie_warning").show();
409 Admin.cancelCurrentFeature();
413 this.commError(SypStrings.ServerError);
417 this.commError(SypStrings.UnreferencedError);
418 Admin.reloadLayer(Admin.dataLayer);
422 this.commError(SypStrings.NochangeError);
426 this.commError(SypStrings.RequestError);
430 this.commError(SypStrings.ToobigError);
431 $("#image_file").parent().html($("#image_file").parent().html());
432 $("#image_file").focus();
435 this.commError(SypStrings.NotimageError);
436 $("#image_file").parent().html($("#image_file").parent().html());
437 $("#image_file").focus();
440 this.commError(SypStrings.UnknownError);
446 switch (xml.documentElement.getAttribute("request")) {
448 this.commSuccess(SypStrings.DelSucces);
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(SypStrings.UnconsistentError);
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(SypStrings.UnconsistentError);
508 this.commSuccess(SypStrings.UpdateSucces);
514 this.commError(SypStrings.UnconsistentError);
519 this.commError(SypStrings.UnconsistentError);
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");
535 /* maintains a queue of ajax queries, so I'm sure they all execute in the same
536 * order they were defined */
542 add: function(query) {
543 this._queue.push(query);
544 if (this._queue.length > 1) {
547 this._runQuery(query);
551 _runQuery: function(query) {
553 $('#api_frame').one("load", function() {
554 self.running = false;
556 if (typeof (query.oncomplete) == "function") {
559 if (this.contentDocument) {
560 body = this.contentDocument.body;
561 } else if (this.contentWindow) {
562 body = this.contentWindow.document.body;
564 body = document.frames[this.id].document.body;
568 query.oncomplete(body.innerHTML);
570 query.oncomplete(null);
574 query.form.attr("action", "api.php");
575 query.form.attr("target", "api_frame");
576 query.form.attr("method", "post");
578 query.form.get(0).submit();
579 if (typeof (query.onsend) == "function") {
584 _reqEnd: function() {
586 if (this._queue.length > 0) {
587 this._reqEnd(this._queue[0]);
595 $("#login_form").submit(this.submit);
596 $("#password").focus().select();
603 submit: function () {
605 pwdMgr.commError("");
607 form: $("#login_form"),
609 $("#pwd_throbber").css("visibility", "visible");
610 $("#login_error").hide();
612 // we need a timeout; otherwise those fields will not be submitted
613 window.setTimeout(function() {
614 // removes focus from #password before disabling it. Otherwise, opera
615 // prevents re-focusing it after re-enabling it.
616 $("#password").blur();
617 $("#login_submit, #password").attr("disabled", "disabled");
620 oncomplete: OpenLayers.Function.bind(pwdMgr.ajaxReply, pwdMgr)
627 ajaxReply: function (data) {
629 $("#pwd_throbber").css("visibility", "hidden");
630 // here, we need a timeout because onsend timeout sometimes has not been triggered yet
631 window.setTimeout(function() {
632 $("#login_submit, #password").removeAttr("disabled");
636 this.commError(SypStrings.ServerError);
637 $("#login_error").show();
638 window.setTimeout(function() {
639 $("#password").focus().select();
643 var xml = new OpenLayers.Format.XML().read(data);
645 switch (xml.documentElement.nodeName.toLowerCase()) {
647 switch (xml.documentElement.getAttribute("reason")) {
649 this.commError(SypStrings.ServerError);
652 this.commError(SypStrings.UnauthorizedError);
655 this.commError(SypStrings.RequestError);
658 this.commError(SypStrings.UnknownError);
661 $("#login_error").show();
662 window.setTimeout(function() {
663 $("#password").focus().select();
668 $("#login_area").hide();
671 this.commError(SypStrings.UnconsistentError);
676 commError: function (message) {
677 $("#login_error").text(message);
679 $("#login_error").show();
681 $("#login_error").hide();
686 $(window).load(function () {
687 // if using .ready, ie triggers an error when trying to access
688 // document.namespaces
690 $("#newfeature_button").click(function () {
691 Admin.addNewFeature();
693 $("#editor_close").click(function () {
694 Admin.cancelCurrentFeature()
696 $("#feature_update").submit(function() {
698 FeatureMgr.save(Admin.currentFeature);
702 $("#feature_delete").submit(function() {
704 FeatureMgr.del(Admin.currentFeature);
708 $("#image_delete").click(function() {
709 $("#img").removeAttr('src');
710 // needs to rebuild element otherwise some browsers still
712 $("#img").parent().html($("#img").parent().html());
713 $("#img").parent().hide();
714 $("#image_delete").hide();
715 $("#image_file").parent().show();