]> dev.renevier.net Git - syp.git/blob - js/admin.js
demo restrictions
[syp.git] / js / admin.js
1 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
2  * license. */
3
4 // drag feature with tolerance
5 OpenLayers.Control.SypDragFeature = OpenLayers.Class (OpenLayers.Control.DragFeature, {
6     startPixel: null,
7     dragStart: null,
8     pixelTolerance : 0,
9     timeTolerance: 300,
10
11     downFeature: function(pixel) {
12         OpenLayers.Control.DragFeature.prototype.downFeature.apply(this, arguments);
13         this.dragStart = (new Date()).getTime(); 
14         this.startPixel = pixel; 
15     },
16
17     doneDragging: function(pixel) {
18         OpenLayers.Control.DragFeature.prototype.doneDragging.apply(this, arguments);
19         // Check tolerance. 
20         var passesTimeTolerance =  
21                     (new Date()).getTime() > this.dragStart + this.timeTolerance; 
22
23         var xDiff = this.startPixel.x - pixel.x; 
24         var yDiff = this.startPixel.y - pixel.y; 
25
26         var passesPixelTolerance =  
27         Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2)) > this.pixelTolerance; 
28
29         if(passesTimeTolerance && passesPixelTolerance){ 
30             this.onComplete(this.feature, pixel);    
31         } else { 
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); 
37         }
38         this.layer.drawFeature(this.feature, "select");
39     },
40
41     moveFeature: function(pixel) {
42         OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments);
43         this.layer.drawFeature(this.feature, "temporary");
44     },
45
46     overFeature: function (feature) {
47         // can only drag and drop currently selected feature
48         if (feature != Admin.currentFeature) {
49             return;
50         }
51         OpenLayers.Control.DragFeature.prototype.overFeature.apply(this, arguments);
52     },
53
54     CLASS_NAME: "OpenLayers.Control.SypDragFeature"
55 });
56
57 var Admin = {
58     Markers: {
59         ICON: "media/marker-normal.png",
60         SELECT_ICON: "media/marker-selected.png",
61         TEMPORARY_ICON: "media/marker-temp.png",
62         HEIGHT: 25
63     },
64
65     map: null,
66     baseLayer: null,
67     dataLayer: null,
68     selFeatureControl: null,
69     moveFeatureControl: null,
70     addFeatureControl: null,
71
72     currentFeature: null,
73     currentFeatureLocation: null,
74
75     init: function () {
76         this.map = new OpenLayers.Map ("map", {
77                 controls:[
78                     new OpenLayers.Control.Navigation (),
79                     new OpenLayers.Control.PanZoom ()
80                 ],
81                 projection: new OpenLayers.Projection("EPSG:900913"),
82                 displayProjection: new OpenLayers.Projection("EPSG:4326")
83          });
84
85          this.baseLayer = this.createBaseLayer ();
86          this.map.addLayer(this.baseLayer);
87
88          this.map.setCenter(new OpenLayers.LonLat(0, 0), 0);
89          if (sypSettings.loggedUser) {
90             this.dataLayer = this.createDataLayer (sypSettings.loggedUser);
91             this.map.addLayer(this.dataLayer);
92             this.reset();
93          }
94     },
95
96     reset: function() {
97         this.addFeatureControl.deactivate();
98         this.moveFeatureControl.deactivate();
99         this.selFeatureControl.activate();
100         this.checkForFeatures();
101         $("#newfeature_button").show().val(SypStrings.AddItem);
102         $("#newfeature_button").unbind("click").click(function () {
103             Admin.addNewFeature();
104         });
105     },
106
107     createBaseLayer: function () {
108         return new OpenLayers.Layer.OSM("OSM");
109     },
110
111     createDataLayer: function (user) {
112         var styleMap = new OpenLayers.StyleMap (
113                         {"default": {
114                              externalGraphic: this.Markers.ICON,
115                              graphicHeight: this.Markers.HEIGHT || 32 
116                                 },
117                          "temporary": { 
118                              externalGraphic: this.Markers.TEMPORARY_ICON,
119                              graphicHeight: this.Markers.HEIGHT || 32 
120                          },
121                          "select": { 
122                              externalGraphic: this.Markers.SELECT_ICON,
123                              graphicHeight: this.Markers.HEIGHT || 32 
124                     }});
125
126         var layer = new OpenLayers.Layer.GML("KML", "items.php?from_user=" + encodeURIComponent(user),
127            {
128             styleMap: styleMap,
129             format: OpenLayers.Format.KML, 
130             projection: this.map.displayProjection,
131             eventListeners: { scope: this,
132                 loadend: this.dataLayerEndLoad
133             }
134        });
135
136         // controls
137         this.selFeatureControl = this.createSelectFeatureControl(layer)
138         this.map.addControl(this.selFeatureControl);
139         this.moveFeatureControl = this.createMoveFeatureControl(layer)
140         this.map.addControl(this.moveFeatureControl);
141         this.addFeatureControl = this.createNewfeatureControl();
142         this.map.addControl(this.addFeatureControl);
143
144         return layer;
145     },
146
147     createMoveFeatureControl: function (layer) {
148         var control = new OpenLayers.Control.SypDragFeature(
149                 layer, {
150                          });
151         return control;
152     },
153
154     createSelectFeatureControl: function (layer) {
155         var control = new OpenLayers.Control.SelectFeature(
156                 layer, {
157                         onSelect: OpenLayers.Function.bind(this.onFeatureSelect, this)
158                          });
159         return control;
160     },
161
162     createNewfeatureControl: function () {
163         var control = new OpenLayers.Control ();
164         var handler = new OpenLayers.Handler.Click(control, {
165                 'click': OpenLayers.Function.bind(FeatureMgr.add, FeatureMgr)
166             });
167         control.handler = handler;
168         return control;
169     },
170
171     onFeatureSelect: function (feature) {
172         this.showEditor(feature);
173         FeatureMgr.reset();
174         this.selFeatureControl.deactivate();
175         this.moveFeatureControl.activate();
176     },
177
178     closeEditor: function() {
179         if ($("#editor").css("display") == "none") {
180             return;
181         }
182         if (this.currentFeature && this.currentFeature.layer) {
183             this.selFeatureControl.unselect(this.currentFeature);
184         }
185         this.currentFeature = null;
186         this.currentFeatureLocation = null;
187         $("#img").removeAttr('src');
188         $("#img").parent().html($("#img").parent().html());
189         $("#img").parent().show();
190         $("#title, #description").val("");
191         $("#editor").hide();
192         // do it once before hidding and once after hidding to work in all cases
193         $("#title, #description").val(""); 
194         $("#image_file").parent().html($("#image_file").parent().html());
195         $(document).unbind("keydown");
196         this.checkForFeatures();
197         this.reset();
198     },
199
200     showEditor: function (feature) {
201         $("#newfeature_button").hide();
202         userMgr.close();
203
204         if (feature.fid) {
205             $("#delete").show();
206         } else {
207             $("#delete").hide();
208         }
209         $(document).unbind("keydown").keydown(function(e) { 
210             if (e.keyCode == 27) {
211                 Admin.cancelCurrentFeature()
212                 e.preventDefault();
213             }
214         });
215         this.currentFeature = feature;
216         this.currentFeatureLocation = new OpenLayers.Pixel(feature.geometry.x, feature.geometry.y);
217         $("#editor").show();
218         $("#instructions").text(SypStrings.DragDropHowto);
219         $("#title").val(feature.attributes.name);
220         var fullDesc = $(feature.attributes.description).parent();
221         $("#description").val(fullDesc.find('p').text());
222         var src = fullDesc.find('img').attr('src');
223         if (src) {
224             $("#img").parent().show();
225             $("#img").attr('src', src);
226             $("#image_file").parent().hide();
227             $("#image_delete").show();
228         } else {
229             $("#img").parent().hide();
230             $("#image_file").parent().show();
231             $("#image_delete").hide();
232         }
233         $("#title").select().focus(); 
234     },
235
236     dataLayerEndLoad: function() {
237         // only set zoom extent once
238         this.dataLayer.events.unregister('loadend', this, this.dataLayerEndLoad);
239         this.dataLayer.events.register('loadend', this, this.checkForFeatures);
240
241         if (!this.checkForFeatures()) {
242             return;
243         }
244
245         var map = this.map;
246         var orig = this.Utils.mbr (this.dataLayer);
247         var centerBounds = new OpenLayers.Bounds();
248
249         var mapProj = map.getProjectionObject();
250         var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
251
252         var bottomLeft = new OpenLayers.LonLat(orig[0],orig[1]);
253         bottomLeft = bottomLeft.transform(sypOrigProj, mapProj);
254         var topRight = new OpenLayers.LonLat(orig[2],orig[3])
255         topRight = topRight.transform(sypOrigProj, mapProj);
256
257         centerBounds.extend(bottomLeft);
258         centerBounds.extend(topRight);
259         map.zoomToExtent(centerBounds);
260     },
261
262     checkForFeatures: function () {
263         var features = this.dataLayer.features;
264         if (features.length != 0) {
265             $("#instructions").text(SypStrings.SelectHowto);
266         }
267         return !!features.length;
268     },
269
270     addNewFeature: function () {
271         userMgr.close();
272         function cancel() {
273             $(document).unbind("keydown");
274             Admin.reset()
275         }
276         $(document).unbind("keydown").keydown(function(e) { 
277             if (e.keyCode == 27) {
278                 e.preventDefault();
279                 cancel();
280             }
281         });
282
283         $("#newfeature_button").val(SypStrings.Cancel);
284         $("#newfeature_button").unbind("click").click(cancel);
285
286         $("#instructions").text(SypStrings.AddHowto);
287         this.selFeatureControl.deactivate();
288         this.addFeatureControl.activate();
289         FeatureMgr.reset();
290     },
291
292     cancelCurrentFeature: function() {
293         if (AjaxMgr.running) {
294             return false;
295         }
296         var feature = this.currentFeature;
297         if (feature) {
298             if (feature.fid) {
299                 FeatureMgr.move (feature, this.currentFeatureLocation);
300             } else {
301                 this.dataLayer.removeFeatures([feature]);
302             }
303         }
304         this.closeEditor();
305         return true;
306     },
307
308     reloadLayer: function (layer) {
309         layer.destroyFeatures();
310         layer.loaded = false;
311         layer.loadGML();
312     },
313
314     Utils: {
315         /* minimum bounds rectangle containing all feature locations.
316          * FIXME: if two features are close, but separated by 180th meridian,
317          * their mbr will span the whole earth. Actually, 179° lon and -170°
318          * lon are considerated very near.
319          */
320         mbr: function (layer) {
321             var features = [];
322             var map = layer.map;
323
324             var mapProj = map.getProjectionObject();
325             var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
326
327             for (var i =0; i < layer.features.length; i++) {
328                 if (layer.features[i].cluster) {
329                     features = features.concat(layer.features[i].cluster);
330                 } else {
331                     features = features.concat(layer.features);
332                 }
333             }
334
335             var minlon = 180;
336             var minlat = 88;
337             var maxlon = -180;
338             var maxlat = -88;
339
340             if (features.length == 0) {
341                 // keep default values
342             } else if (features.length == 1) {
343                 // in case there's only one feature, we show an area of at least 
344                 // 4 x 4 degrees
345                 var pos = features[0].geometry.getBounds().getCenterLonLat().clone();
346                 var lonlat = pos.transform(mapProj, sypOrigProj);
347
348                 minlon = Math.max (lonlat.lon - 2, -180);
349                 maxlon = Math.min (lonlat.lon + 2, 180);
350                 minlat = Math.max (lonlat.lat - 2, -90);
351                 maxlat = Math.min (lonlat.lat + 2, 90);
352             } else {
353                 for (var i = 0; i < features.length; i++) {
354                     var pos = features[i].geometry.getBounds().getCenterLonLat().clone();
355                     var lonlat = pos.transform(mapProj, sypOrigProj);
356                     minlon = Math.min (lonlat.lon, minlon);
357                     minlat = Math.min (lonlat.lat, minlat);
358                     maxlon = Math.max (lonlat.lon, maxlon);
359                     maxlat = Math.max (lonlat.lat, maxlat);
360                 }
361             }
362
363             return [minlon, minlat, maxlon, maxlat];
364
365         },
366
367
368         escapeHTML: function (str) {
369             if (!str) {
370                 return "";
371             }
372             return str.
373              replace(/&/gm, '&amp;').
374              replace(/'/gm, '&#39;').
375              replace(/"/gm, '&quot;').
376              replace(/>/gm, '&gt;').
377              replace(/</gm, '&lt;');
378         },
379
380         startsWith: function (str, prefix) {
381             return (str.slice(0, prefix.length) == prefix);
382         },
383
384         indexOf: function (array, item) {
385             if (array.indexOf !== undefined) {
386                 return array.indexOf(item);
387             } else {
388                 return OpenLayers.Util.indexOf(array, item);
389             }
390         }
391     }
392 }
393
394 var FeatureMgr = {
395     reset: function() {
396         this.commError("");
397     },
398
399     add: function(evt) {
400         alert (SypStrings.DisabledForDemo);
401         $(document).unbind("keydown");
402         Admin.reset()
403         return;
404         var map = Admin.map;
405         var pos = map.getLonLatFromViewPortPx(evt.xy);
406         feature = this.update (null, pos, "", "", "");
407         Admin.addFeatureControl.deactivate();
408         Admin.selFeatureControl.select(feature);
409     },
410
411     move: function (feature, aLocation) {
412         if (!feature || !aLocation) {
413             return;
414         }
415         var curLoc = feature.geometry;
416         feature.geometry.move(aLocation.x - curLoc.x, aLocation.y - curLoc.y);
417         feature.layer.drawFeature(feature); 
418     },
419
420     update: function(feature, lonlat, imgurl, title, description) {
421         var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
422         if (!feature) {
423             feature = new OpenLayers.Feature.Vector(point);
424             Admin.dataLayer.addFeatures([feature]);
425         } else {
426             this.move (feature, point);
427         }
428         feature.attributes.name = title;
429         feature.attributes.description = "<p>" + Admin.Utils.escapeHTML(description) + "</p>"
430                                 + "<img src=\"" + imgurl + "\">"
431         return feature;
432     },
433
434     del: function (feature) {
435         alert (SypStrings.DisabledForDemo);
436         return;
437         var form = $("#feature_delete");
438         form.find('input[name="fid"]').val(feature.fid);
439         AjaxMgr.add({
440             form: form,
441             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
442             throbberid: "editor_throbber"
443         });
444     },
445
446     save: function (feature) {
447         var x = feature.geometry.x;
448         var y = feature.geometry.y;
449
450         var mapProj = feature.layer.map.getProjectionObject();
451         var lonlat = new OpenLayers.LonLat(x, y).
452                                     transform(mapProj,
453                                               new OpenLayers.Projection("EPSG:4326"));
454         var form = $("#feature_update");
455         form.find('input[name="lon"]').val(lonlat.lon);
456         form.find('input[name="lat"]').val(lonlat.lat);
457         form.find('input[name="fid"]').val(feature.fid);
458         form.find('input[name="keep_img"]').val(
459             $("#img").attr("src") ? "yes": "no"
460         );
461
462         if (feature.fid) {
463             form.find('input[name="request"]').val("update");
464         } else {
465             form.find('input[name="request"]').val("add");
466         }
467         AjaxMgr.add({
468             form: form,
469             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
470             throbberid: "editor_throbber"
471         });
472     },
473
474     ajaxReply: function (data) {
475         if (!data) {
476             this.commError(SypStrings.ServerError);
477             return;
478         }
479
480         var xml = new OpenLayers.Format.XML().read(data);
481         if (!xml.documentElement) {
482             this.commError(SypStrings.UnconsistentError);
483             $("title").focus();
484             return;
485         }
486
487         switch (xml.documentElement.nodeName.toLowerCase()) {
488             case "error":
489                 switch (xml.documentElement.getAttribute("reason")) {
490                     case "unauthorized":
491                         pwdMgr.reset();
492                         $("#cookie_warning").show();
493                         this.reset();
494                         Admin.cancelCurrentFeature();
495                         Admin.reset();
496                         userMgr.uninit();
497                     break;
498                     case "server":
499                         this.commError(SypStrings.ServerError);
500                         $("title").focus();
501                     break;
502                     case "unreferenced":
503                         this.commError(SypStrings.UnreferencedError);
504                         Admin.reloadLayer(Admin.dataLayer);
505                         Admin.closeEditor();
506                     break;
507                     case "nochange":
508                         this.commError(SypStrings.NochangeError);
509                         Admin.closeEditor();
510                     break;
511                     case "request":
512                         this.commError(SypStrings.RequestError);
513                         $("title").focus();
514                     break;
515                     case "toobig":
516                         this.commError(SypStrings.ToobigError);
517                         $("#image_file").parent().html($("#image_file").parent().html());
518                         $("#image_file").focus();
519                     break;
520                     case "notimage":
521                         this.commError(SypStrings.NotimageError);
522                         $("#image_file").parent().html($("#image_file").parent().html());
523                         $("#image_file").focus();
524                     break;
525                     default:
526                         this.commError(SypStrings.UnconsistentError);
527                         $("title").focus();
528                     break;
529                 }
530             break;
531             case "success":
532                 switch (xml.documentElement.getAttribute("request")) {
533                     case "del":
534                         this.commSuccess(SypStrings.DelSucces);
535                         var someFeature = false;
536                         var self = this;
537                         $.each($(xml).find("FEATURE,feature"), function () {
538                              someFeature = true;
539                              var id = parseFloat($(this).find("ID:first,id:first").text());
540                              if ((id === null) || isNaN (id)) {
541                                 return;;
542                              }
543                              var features = Admin.dataLayer.features;
544                              for (var idx = 0; idx < features.length; idx++) {
545                                  if (features[idx].fid == id) {
546                                      Admin.dataLayer.removeFeatures([features[idx]]);
547                                  }
548                              }
549                         });
550                         if (someFeature == false) {
551                             this.commError(SypStrings.UnconsistentError);
552                         } else {
553                             Admin.closeEditor();
554                         }
555                     break;
556                     case "update":
557                     case "add":
558                         var someFeature = false;
559                         var self = this;
560                         $.each($(xml).find("FEATURE,feature"), function () {
561                                 someFeature = true;
562                                 var id = parseFloat($(this).find("ID:first,id:first").text());
563                                 if ((id === null) || isNaN (id)) {
564                                     return;;
565                                 }
566
567                                 var lon = parseFloat($(this).find("LON:first,lon:first").text());
568                                 if ((typeof (lon) != "number") || isNaN (lon) ||
569                                         (lon < -180) || (lon > 180)) {
570                                     return;;
571                                 }
572
573                                 var lat = parseFloat($(this).find("LAT:first,lat:first").text());
574                                 if ((typeof (lat) != "number") || isNaN (lat) ||
575                                         (lat < -90) || (lat > 90)) {
576                                     return;;
577                                 }
578
579                                 var mapProj = Admin.map.getProjectionObject();
580                                 var lonlat = new OpenLayers.LonLat (lon, lat).
581                                                 transform( new OpenLayers.Projection("EPSG:4326"), mapProj);
582
583                                 var imgurl = $(this).find("IMGURL:first,imgurl:first").text();
584                                 var title = $(this).find("HEADING:first,heading:first").text();
585                                 var description = $(this).find("DESCRIPTION:first,description:first").text();
586
587                                 feature = self.update (Admin.currentFeature, lonlat, imgurl, title, description); 
588                                 feature.fid = id;
589                         });
590
591                         if (someFeature == false) {
592                             this.commError(SypStrings.UnconsistentError);
593                         } else {
594                             this.commSuccess(SypStrings.UpdateSucces);
595                             Admin.closeEditor();
596                         }
597
598                     break;
599                     default:
600                         this.commError(SypStrings.UnconsistentError);
601                    break;
602                 }
603             break;
604             default:
605                 this.commError(SypStrings.UnconsistentError);
606             break;
607         }
608     },
609
610     commSuccess: function (message) {
611         $("#server_comm").text(message);
612         $("#server_comm").removeClass("error success").addClass("success");
613     },
614
615     commError: function (message) {
616         $("#server_comm").text(message);
617         $("#server_comm").removeClass("error success").addClass("error");
618     }
619 }
620
621 /* maintains a queue of ajax queries, so I'm sure they all execute in the same
622  * order they were defined */
623 var AjaxMgr = {
624     _queue: [],
625
626     running: false,
627
628     add: function(query) {
629         this._queue.push(query);
630         if (this._queue.length > 1) {
631             return;
632         } else {
633             this._runQuery(query);
634         }
635     },
636
637     _runQuery: function(query) {
638         var self = this;
639         $('#api_frame').one("load", function() {
640             self.running = false;
641             self._reqEnd();
642             if (query.throbberid) {
643                 $("#" + query.throbberid).css("visibility", "hidden");
644             }
645             if (typeof (query.oncomplete) == "function") {
646                 var body = null;
647                 try {
648                     if (this.contentDocument) {
649                         body = this.contentDocument.body;
650                     } else if (this.contentWindow) {
651                         body = this.contentWindow.document.body;
652                     } else {
653                         body = document.frames[this.id].document.body;
654                     }
655                 } catch (e) {}
656                     if (body) {
657                         query.oncomplete(body.innerHTML);
658                     } else {
659                         query.oncomplete(null);
660                     }
661             }
662         });
663         query.form.attr("action", "api.php");
664         query.form.attr("target", "api_frame");
665         query.form.attr("method", "post");
666         this.running = true;
667         query.form.get(0).submit();
668         if (query.throbberid) {
669             $("#" + query.throbberid).css("visibility", "visible");
670         }
671         if (typeof (query.onsend) == "function") {
672             query.onsend();
673         }
674     },
675
676     _reqEnd: function() {
677         this._queue.shift();
678         if (this._queue.length > 0) {
679             this._reqEnd(this._queue[0]);
680         }
681     }
682 }
683
684 var pwdMgr = {
685
686     init: function () {
687         $("#login_form").submit(this.submit);
688         $("#user").focus().select();
689     },
690
691     reset: function() {
692         this.commError ("");
693         $("#login_area").show();
694         $("#password").val("");
695         $("#user").val(sypSettings.loggedUser).focus().select();
696     },
697
698     submit: function () {
699         try {
700             pwdMgr.commError("");
701             var req = {
702                 form:  $("#login_form"),
703                 throbberid: "pwd_throbber",
704                 onsend: function() {
705                     $("#login_error").hide();
706
707                     // we need a timeout; otherwise those fields will not be submitted
708                     window.setTimeout(function() { 
709                             // removes focus from #password before disabling it. Otherwise, opera
710                             // prevents re-focusing it after re-enabling it.
711                             $("#user, #password").blur(); 
712                             $("#login_submit, #user, #password").attr("disabled", "disabled");
713                     }, 0)
714                 },
715                 oncomplete: OpenLayers.Function.bind(pwdMgr.ajaxReply, pwdMgr)
716             };
717             AjaxMgr.add(req);
718         } catch(e) {}
719         return false;
720     },
721
722     ajaxReply: function (data) {
723         // here, we need a timeout because onsend timeout sometimes has not been triggered yet
724         window.setTimeout(function() {
725             $("#login_submit, #user, #password").removeAttr("disabled");
726         }, 0);
727
728         if (!data) {
729             this.commError(SypStrings.ServerError);
730             $("#login_error").show();
731             window.setTimeout(function() {
732                     $("#user").focus().select();
733             }, 0);
734             return;
735         }
736
737         var xml = new OpenLayers.Format.XML().read(data);
738         if (!xml.documentElement) {
739             this.commError(SypStrings.UnconsistentError);
740             $("#login_error").show();
741             window.setTimeout(function() {
742                     $("#user").focus().select();
743             }, 0);
744         }
745
746         switch (xml.documentElement.nodeName.toLowerCase()) {
747             case "error":
748                 switch (xml.documentElement.getAttribute("reason")) {
749                     case "server":
750                         this.commError(SypStrings.ServerError);
751                     break;
752                     case "unauthorized":
753                         this.commError(SypStrings.UnauthorizedError);
754                     break;
755                     case "request":
756                         this.commError(SypStrings.RequestError);
757                     break;
758                     default:
759                         this.commError(SypStrings.UnconsistentError);
760                     break;
761                 }
762                 $("#login_error").show();
763                 window.setTimeout(function() {
764                         $("#user").focus().select();
765                 }, 0);
766             break;
767             case "success":
768                 $("#login_area").hide();
769
770                 user = $(xml).find("USER,user").text();
771                 sypSettings.loggedUser = user;
772
773                 if (sypSettings.loggedUser == "admin")  {
774                     userMgr.init();
775                 }
776
777                 if (Admin.selFeatureControl) {
778                     Admin.selFeatureControl.destroy();
779                 }
780                 if (Admin.moveFeatureControl) {
781                     Admin.moveFeatureControl.destroy();
782                 }
783                 if (Admin.addFeatureControl) {
784                     Admin.addFeatureControl.destroy();
785                 }
786                 if (Admin.dataLayer) {
787                     Admin.dataLayer.destroy();
788                 }
789
790                 Admin.dataLayer = Admin.createDataLayer(user);
791                 Admin.map.addLayer(Admin.dataLayer);
792                 Admin.reset();
793
794             break;
795             default:
796                 this.commError(SypStrings.UnconsistentError);
797             break;
798         }
799     },
800
801     commError: function (message) {
802         $("#login_error").text(message);
803         if (message) {
804             $("#login_error").show();
805         } else {
806             $("#login_error").hide();
807         }
808     }
809 }
810
811 var userMgr = {
812     _adduserDisplayed: false,
813     _changepassDisplayed: false,
814
815     init: function() {
816         $("#user_close").unbind("click").click(function () {
817             userMgr.close()
818         });
819
820         $("#change_pass").unbind("click").click(function() {
821             userMgr.toggleChangePass();
822             return false;
823         });
824         $("#changepass").unbind("submit").submit(function() {
825             try {
826                 userMgr.changepass();
827             } catch(e) {}
828             return false;
829         });
830
831         if (sypSettings.loggedUser != "admin") {
832             return;
833         }
834
835         $("#add_user").show();
836         $("#add_user").unbind("click").click(function () {
837             userMgr.toggleAddUser();
838             return false;
839         });
840         $("#newuser").unbind("submit").submit(function() {
841             try {
842                 userMgr.add();
843             } catch(e) {}
844             return false;
845         });
846
847     },
848
849     disableForms: function() {
850         $("#newuser_name, #newuser_password, #newuser_password_confirm, #newuser_submit").attr("disabled", "disabled");
851         $("#pass_current, #pass_new, #pass_new_confirm, #pass_submit").attr("disabled", "disabled");
852     },
853
854     enableForms: function() {
855         $("#newuser_name, #newuser_password, #newuser_password_confirm, #newuser_submit").removeAttr("disabled");
856         $("#pass_current, #pass_new, #pass_new_confirm, #pass_submit").removeAttr("disabled");
857     },
858
859     resetForms: function() {
860         $("#newuser_name, #newuser_password, #newuser_password_confirm").val("");
861         $("#pass_current, #pass_new, #pass_new_confirm").val("");
862     },
863
864     uninit: function() {
865         this.close();
866         $("#add_user").unbind("click");
867         $("#add_user").hide();
868         $("#change_pass").unbind("click");
869         $("#user_close").unbind("click");
870         $("#newuser").unbind("submit");
871         $("#changepass").unbind("submit");
872     },
873
874     close: function() {
875         this.closeChangePass();
876         this.closeAddUser();
877     },
878
879     toggleChangePass: function() {
880         if (this._changepassDisplayed) {
881             this.closeChangePass();
882         } else {
883             this.showChangePass();
884         }
885     },
886
887     showChangePass: function() {
888         if (!Admin.cancelCurrentFeature()) {
889             return;
890         }
891         this.closeAddUser();
892
893         $(document).unbind("keydown").keydown(function(e) { 
894             if (e.keyCode == 27) {
895                 userMgr.closeChangePass()
896                 e.preventDefault();
897             }
898         });
899
900         this.resetForms();
901         this.enableForms();
902         $("#user_area, #changepass").show();
903         this.commError("");
904
905         // XXX: setTimeout needed because otherwise, map becomes hidden in IE. Why ??
906         window.setTimeout(function() { 
907             $("#pass_current").focus();
908         }, 0);
909
910         this._changepassDisplayed = true;
911     },
912
913     closeChangePass: function() {
914         if (!this._changepassDisplayed) {
915             return;
916         }
917         $("#user_area, #changepass").hide();
918         $(document).unbind("keydown");
919         this._changepassDisplayed = false;
920     },
921
922     changepass: function() {
923         var newpass = $("#pass_new").val();
924         var newpass_confirm = $("#pass_new_confirm").val();
925         if (newpass != newpass_confirm) {
926             this.commError(SypStrings.userPasswordmatchError);
927             $("#pass_new").focus().select();
928             return;
929         }
930
931         if (!newpass) {
932             this.commError(SypStrings.emptyPasswordError);
933             $("#pass_new").focus().select();
934             return;
935         }
936
937         var curpass = $("#pass_current").val();
938         if (newpass == curpass) {
939             this.commError(SypStrings.changeSamePass);
940             $("#pass_new").focus().select();
941             return;
942         }
943
944         this.commError("");
945
946         this.disableForms();
947         alert (SypStrings.DisabledForDemo);
948         return;
949
950         AjaxMgr.add({
951             form: $("#changepass"),
952             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
953             throbberid: "user_throbber",
954             onsend: function() { 
955                 // we need a timeout; otherwise those fields will not be submitted
956                 window.setTimeout(function() {
957                     // removes focus from #password before disabling it. Otherwise, opera
958                     // prevents re-focusing it after re-enabling it.
959                     $("#pass_current, #pass_new, #pass_new_confirm").blur(); 
960                     userMgr.disableForms();
961                 }, 0);
962             }
963         });
964     },
965
966     toggleAddUser: function() {
967         if (this._adduserDisplayed) {
968             this.closeAddUser();
969         } else {
970             this.showAddUser();
971         }
972     },
973
974     showAddUser: function() {
975         if (!Admin.cancelCurrentFeature()) {
976             return;
977         }
978
979         this.closeChangePass();
980
981         $(document).unbind("keydown").keydown(function(e) { 
982             if (e.keyCode == 27) {
983                 userMgr.closeAddUser()
984                 e.preventDefault();
985             }
986         });
987
988         $("#user_area, #newuser").show();
989         this.resetForms();
990         this.enableForms();
991         this.commError("");
992
993         // XXX: setTimeout needed because otherwise, map becomes hidden in IE. Why ??
994         window.setTimeout(function() { 
995             $("#newuser_name").focus();
996         }, 0);
997
998         this._adduserDisplayed = true;
999     },
1000
1001     closeAddUser: function() {
1002         if (!this._adduserDisplayed) {
1003             return;
1004         }
1005         $("#user_area, #newuser").hide();
1006         $(document).unbind("keydown");
1007         this._adduserDisplayed = false;
1008     },
1009
1010     add: function() {
1011         var newuser_name = $("#newuser_name").val();
1012         if (!newuser_name) {
1013             this.commError(SypStrings.newUserNonameError);
1014             $("#newuser_name").focus();
1015             return;
1016         }
1017
1018         var newuser_pass = $("#newuser_password").val();
1019         var newuser_pass_confirm = $("#newuser_password_confirm").val();
1020         if (newuser_pass != newuser_pass_confirm) {
1021             this.commError(SypStrings.userPasswordmatchError);
1022             $("#newuser_password").focus().select();
1023             return;
1024         }
1025
1026         if (!newuser_pass) {
1027             this.commError(SypStrings.emptyPasswordError);
1028             $("#pass_new").focus().select();
1029             return;
1030         }
1031
1032         this.commError("");
1033         this.disableForms();
1034         alert (SypStrings.DisabledForDemo);
1035         return;
1036
1037         AjaxMgr.add({
1038             form: $("#newuser"),
1039             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
1040             throbberid: "user_throbber",
1041             onsend: function() { 
1042                 // we need a timeout; otherwise those fields will not be submitted
1043                 window.setTimeout(function() {
1044                     // removes focus from #password before disabling it. Otherwise, opera
1045                     // prevents re-focusing it after re-enabling it.
1046                     $("#newuser_name, #newuser_password, #newuser_password_confirm").blur(); 
1047                     userMgr.disableForms();
1048                 }, 0);
1049             }
1050         });
1051     },
1052
1053     ajaxReply: function (data) {
1054         if (!data) {
1055             // here, we need a timeout because onsend timeout sometimes has not been triggered yet
1056             var self = this;
1057             window.setTimeout(function() {
1058                 self.enableForms();
1059              }, 0);
1060             this.commError(SypStrings.ServerError);
1061             return;
1062         }
1063
1064         var xml = new OpenLayers.Format.XML().read(data);
1065         if (!xml.documentElement) {
1066             // here, we need a timeout because onsend timeout sometimes has not been triggered yet
1067             var self = this;
1068             window.setTimeout(function() {
1069                 self.enableForms();
1070              }, 0);
1071             this.commError(SypStrings.UnconsistentError);
1072             return;
1073         }
1074
1075         var needFormEnabling = true;
1076         var focusEl = null;
1077
1078         switch (xml.documentElement.nodeName.toLowerCase()) {
1079             case "error":
1080                 switch (xml.documentElement.getAttribute("reason")) {
1081                     case "unauthorized":
1082                         pwdMgr.reset();
1083                         $("#cookie_warning").show();
1084                         Admin.reset();
1085                         this.uninit();
1086                     break;
1087                     case "server":
1088                         this.commError(SypStrings.ServerError);
1089                         if (this._adduserDisplayed) {
1090                             focusEl = $("#newuser_name");
1091                         } else if (this._changepassDisplayed) {
1092                             focusEl = $("#pass_current");
1093                         }
1094                     break;
1095                     case "request":
1096                         this.commError(SypStrings.RequestError);
1097                         if (this._adduserDisplayed) {
1098                             focusEl = $("#newuser_name");
1099                         } else if (this._changepassDisplayed) {
1100                             focusEl = $("#pass_current");
1101                         }
1102                     break;
1103                     case "wrongpass":
1104                         this.commError(SypStrings.changePassBadPass);
1105                         focusEl = $("#pass_current");
1106                     break;
1107                     case "newuser_exists":
1108                         this.commError(SypStrings.newUserExistsError);
1109                         focusEl = $("#newuser_name");
1110                     break;
1111                     default:
1112                         this.commError(SypStrings.UnconsistentError);
1113                         if (this._adduserDisplayed) {
1114                             focusEl = $("#newuser_name");
1115                         } else if (this._changepassDisplayed) {
1116                             focusEl = $("#pass_current");
1117                         }
1118                     break;
1119                 }
1120             break;
1121             case "success":
1122                 switch (xml.documentElement.getAttribute("request")) {
1123                     case "newuser":
1124                         this.commSuccess(SypStrings.newUserSuccess);
1125                         needFormEnabling = false;
1126                     break;
1127                     case "changepass":
1128                         this.commSuccess(SypStrings.changePassSuccess);
1129                         needFormEnabling = false;
1130                     break;
1131                     default:
1132                         this.commError(SypStrings.UnconsistentError);
1133                         focusEl = $("newuser_name");
1134                     break;
1135                 }
1136             break;
1137             default:
1138                 this.commError(SypStrings.UnconsistentError);
1139                 focusEl = $("newuser_name");
1140             break;
1141         }
1142
1143         if (needFormEnabling) {
1144             // here, we need a timeout because onsend timeout sometimes has not been triggered yet
1145             var self = this;
1146             window.setTimeout(function() {
1147                 self.enableForms();
1148                 if (focusEl) {
1149                     focusEl.select().focus();
1150                 }
1151              }, 0);
1152         } else {
1153             if (focusEl) {
1154                 focusEl.focus().select();
1155             }
1156         }
1157
1158     },
1159
1160     commSuccess: function (message) {
1161         $("#user_comm").text(message);
1162         $("#user_comm").removeClass("error success").addClass("success");
1163     },
1164
1165     commError: function (message) {
1166         $("#user_comm").text(message);
1167         $("#user_comm").removeClass("error success").addClass("error");
1168     }
1169 }
1170
1171 $(window).load(function () {
1172     // if using .ready, ie triggers an error when trying to access
1173     // document.namespaces
1174     pwdMgr.init();
1175     $("#newfeature_button").click(function () {
1176         Admin.addNewFeature();
1177     });
1178     $("#editor_close").click(function () {
1179         Admin.cancelCurrentFeature()
1180     });
1181     $("#feature_update").submit(function() {
1182         try {
1183             FeatureMgr.save(Admin.currentFeature);
1184         } catch(e) {}
1185         return false;
1186     });
1187     $("#feature_delete").submit(function() {
1188         try {
1189             FeatureMgr.del(Admin.currentFeature);
1190         } catch(e) {}
1191         return false;
1192     });
1193     $("#image_delete").click(function() {
1194             alert (SypStrings.DisabledForDemo);
1195             return;
1196             $("#img").removeAttr('src');
1197             // needs to rebuild element otherwise some browsers still
1198             // display image.
1199             $("#img").parent().html($("#img").parent().html());
1200             $("#img").parent().hide();
1201             $("#image_delete").hide();
1202             $("#image_file").parent().show();
1203     });
1204
1205     userMgr.init();
1206     Admin.init();
1207 });