]> dev.renevier.net Git - syp.git/blob - js/admin.js
removes hardcoded "cancel" string
[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
273         function cancel() {
274             $(document).unbind("keydown");
275             Admin.reset()
276         }
277         $(document).unbind("keydown").keydown(function(e) { 
278             if (e.keyCode == 27) {
279                 e.preventDefault();
280                 cancel();
281             }
282         });
283
284         $("#newfeature_button").val(SypStrings.Cancel);
285         $("#newfeature_button").unbind("click").click(cancel);
286
287         $("#instructions").text(SypStrings.AddHowto);
288         this.selFeatureControl.deactivate();
289         this.addFeatureControl.activate();
290         FeatureMgr.reset();
291     },
292
293     cancelCurrentFeature: function() {
294         if (AjaxMgr.running) {
295             return false;
296         }
297         var feature = this.currentFeature;
298         if (feature) {
299             if (feature.fid) {
300                 FeatureMgr.move (feature, this.currentFeatureLocation);
301             } else {
302                 this.dataLayer.removeFeatures([feature]);
303             }
304         }
305         this.closeEditor();
306         return true;
307     },
308
309     reloadLayer: function (layer) {
310         layer.destroyFeatures();
311         layer.loaded = false;
312         layer.loadGML();
313     },
314
315     Utils: {
316         /* minimum bounds rectangle containing all feature locations.
317          * FIXME: if two features are close, but separated by 180th meridian,
318          * their mbr will span the whole earth. Actually, 179° lon and -170°
319          * lon are considerated very near.
320          */
321         mbr: function (layer) {
322             var features = [];
323             var map = layer.map;
324
325             var mapProj = map.getProjectionObject();
326             var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
327
328             for (var i =0; i < layer.features.length; i++) {
329                 if (layer.features[i].cluster) {
330                     features = features.concat(layer.features[i].cluster);
331                 } else {
332                     features = features.concat(layer.features);
333                 }
334             }
335
336             var minlon = 180;
337             var minlat = 88;
338             var maxlon = -180;
339             var maxlat = -88;
340
341             if (features.length == 0) {
342                 // keep default values
343             } else if (features.length == 1) {
344                 // in case there's only one feature, we show an area of at least 
345                 // 4 x 4 degrees
346                 var pos = features[0].geometry.getBounds().getCenterLonLat().clone();
347                 var lonlat = pos.transform(mapProj, sypOrigProj);
348
349                 minlon = Math.max (lonlat.lon - 2, -180);
350                 maxlon = Math.min (lonlat.lon + 2, 180);
351                 minlat = Math.max (lonlat.lat - 2, -90);
352                 maxlat = Math.min (lonlat.lat + 2, 90);
353             } else {
354                 for (var i = 0; i < features.length; i++) {
355                     var pos = features[i].geometry.getBounds().getCenterLonLat().clone();
356                     var lonlat = pos.transform(mapProj, sypOrigProj);
357                     minlon = Math.min (lonlat.lon, minlon);
358                     minlat = Math.min (lonlat.lat, minlat);
359                     maxlon = Math.max (lonlat.lon, maxlon);
360                     maxlat = Math.max (lonlat.lat, maxlat);
361                 }
362             }
363
364             return [minlon, minlat, maxlon, maxlat];
365
366         },
367
368
369         escapeHTML: function (str) {
370             if (!str) {
371                 return "";
372             }
373             return str.
374              replace(/&/gm, '&amp;').
375              replace(/'/gm, '&#39;').
376              replace(/"/gm, '&quot;').
377              replace(/>/gm, '&gt;').
378              replace(/</gm, '&lt;');
379         },
380
381         startsWith: function (str, prefix) {
382             return (str.slice(0, prefix.length) == prefix);
383         },
384
385         indexOf: function (array, item) {
386             if (array.indexOf !== undefined) {
387                 return array.indexOf(item);
388             } else {
389                 return OpenLayers.Util.indexOf(array, item);
390             }
391         }
392     }
393 }
394
395 var FeatureMgr = {
396     reset: function() {
397         this.commError("");
398     },
399
400     add: function(evt) {
401         var map = Admin.map;
402         var pos = map.getLonLatFromViewPortPx(evt.xy);
403         feature = this.update (null, pos, "", "", "");
404         Admin.addFeatureControl.deactivate();
405         Admin.selFeatureControl.select(feature);
406     },
407
408     move: function (feature, aLocation) {
409         if (!feature || !aLocation) {
410             return;
411         }
412         var curLoc = feature.geometry;
413         feature.geometry.move(aLocation.x - curLoc.x, aLocation.y - curLoc.y);
414         feature.layer.drawFeature(feature); 
415     },
416
417     update: function(feature, lonlat, imgurl, title, description) {
418         var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
419         if (!feature) {
420             feature = new OpenLayers.Feature.Vector(point);
421             Admin.dataLayer.addFeatures([feature]);
422         } else {
423             this.move (feature, point);
424         }
425         feature.attributes.name = title;
426         feature.attributes.description = "<p>" + Admin.Utils.escapeHTML(description) + "</p>"
427                                 + "<img src=\"" + imgurl + "\">"
428         return feature;
429     },
430
431     del: function (feature) {
432         var form = $("#feature_delete");
433         form.find('input[name="fid"]').val(feature.fid);
434         AjaxMgr.add({
435             form: form,
436             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
437             throbberid: "editor_throbber"
438         });
439     },
440
441     save: function (feature) {
442         var x = feature.geometry.x;
443         var y = feature.geometry.y;
444
445         var mapProj = feature.layer.map.getProjectionObject();
446         var lonlat = new OpenLayers.LonLat(x, y).
447                                     transform(mapProj,
448                                               new OpenLayers.Projection("EPSG:4326"));
449         var form = $("#feature_update");
450         form.find('input[name="lon"]').val(lonlat.lon);
451         form.find('input[name="lat"]').val(lonlat.lat);
452         form.find('input[name="fid"]').val(feature.fid);
453         form.find('input[name="keep_img"]').val(
454             $("#img").attr("src") ? "yes": "no"
455         );
456
457         if (feature.fid) {
458             form.find('input[name="request"]').val("update");
459         } else {
460             form.find('input[name="request"]').val("add");
461         }
462         AjaxMgr.add({
463             form: form,
464             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
465             throbberid: "editor_throbber"
466         });
467     },
468
469     ajaxReply: function (data) {
470         if (!data) {
471             this.commError(SypStrings.ServerError);
472             return;
473         }
474
475         var xml = new OpenLayers.Format.XML().read(data);
476         if (!xml.documentElement) {
477             this.commError(SypStrings.UnconsistentError);
478             $("title").focus();
479             return;
480         }
481
482         switch (xml.documentElement.nodeName.toLowerCase()) {
483             case "error":
484                 switch (xml.documentElement.getAttribute("reason")) {
485                     case "unauthorized":
486                         pwdMgr.reset();
487                         $("#cookie_warning").show();
488                         this.reset();
489                         Admin.cancelCurrentFeature();
490                         Admin.reset();
491                         userMgr.uninit();
492                     break;
493                     case "server":
494                         this.commError(SypStrings.ServerError);
495                         $("title").focus();
496                     break;
497                     case "unreferenced":
498                         this.commError(SypStrings.UnreferencedError);
499                         Admin.reloadLayer(Admin.dataLayer);
500                         Admin.closeEditor();
501                     break;
502                     case "nochange":
503                         this.commError(SypStrings.NochangeError);
504                         Admin.closeEditor();
505                     break;
506                     case "request":
507                         this.commError(SypStrings.RequestError);
508                         $("title").focus();
509                     break;
510                     case "toobig":
511                         this.commError(SypStrings.ToobigError);
512                         $("#image_file").parent().html($("#image_file").parent().html());
513                         $("#image_file").focus();
514                     break;
515                     case "notimage":
516                         this.commError(SypStrings.NotimageError);
517                         $("#image_file").parent().html($("#image_file").parent().html());
518                         $("#image_file").focus();
519                     break;
520                     default:
521                         this.commError(SypStrings.UnconsistentError);
522                         $("title").focus();
523                     break;
524                 }
525             break;
526             case "success":
527                 switch (xml.documentElement.getAttribute("request")) {
528                     case "del":
529                         this.commSuccess(SypStrings.DelSucces);
530                         var someFeature = false;
531                         var self = this;
532                         $.each($(xml).find("FEATURE,feature"), function () {
533                              someFeature = true;
534                              var id = parseFloat($(this).find("ID:first,id:first").text());
535                              if ((id === null) || isNaN (id)) {
536                                 return;;
537                              }
538                              var features = Admin.dataLayer.features;
539                              for (var idx = 0; idx < features.length; idx++) {
540                                  if (features[idx].fid == id) {
541                                      Admin.dataLayer.removeFeatures([features[idx]]);
542                                  }
543                              }
544                         });
545                         if (someFeature == false) {
546                             this.commError(SypStrings.UnconsistentError);
547                         } else {
548                             Admin.closeEditor();
549                         }
550                     break;
551                     case "update":
552                     case "add":
553                         var someFeature = false;
554                         var self = this;
555                         $.each($(xml).find("FEATURE,feature"), function () {
556                                 someFeature = true;
557                                 var id = parseFloat($(this).find("ID:first,id:first").text());
558                                 if ((id === null) || isNaN (id)) {
559                                     return;;
560                                 }
561
562                                 var lon = parseFloat($(this).find("LON:first,lon:first").text());
563                                 if ((typeof (lon) != "number") || isNaN (lon) ||
564                                         (lon < -180) || (lon > 180)) {
565                                     return;;
566                                 }
567
568                                 var lat = parseFloat($(this).find("LAT:first,lat:first").text());
569                                 if ((typeof (lat) != "number") || isNaN (lat) ||
570                                         (lat < -90) || (lat > 90)) {
571                                     return;;
572                                 }
573
574                                 var mapProj = Admin.map.getProjectionObject();
575                                 var lonlat = new OpenLayers.LonLat (lon, lat).
576                                                 transform( new OpenLayers.Projection("EPSG:4326"), mapProj);
577
578                                 var imgurl = $(this).find("IMGURL:first,imgurl:first").text();
579                                 var title = $(this).find("HEADING:first,heading:first").text();
580                                 var description = $(this).find("DESCRIPTION:first,description:first").text();
581
582                                 feature = self.update (Admin.currentFeature, lonlat, imgurl, title, description); 
583                                 feature.fid = id;
584                         });
585
586                         if (someFeature == false) {
587                             this.commError(SypStrings.UnconsistentError);
588                         } else {
589                             this.commSuccess(SypStrings.UpdateSucces);
590                             Admin.closeEditor();
591                         }
592
593                     break;
594                     default:
595                         this.commError(SypStrings.UnconsistentError);
596                    break;
597                 }
598             break;
599             default:
600                 this.commError(SypStrings.UnconsistentError);
601             break;
602         }
603     },
604
605     commSuccess: function (message) {
606         $("#server_comm").text(message);
607         $("#server_comm").removeClass("error success").addClass("success");
608     },
609
610     commError: function (message) {
611         $("#server_comm").text(message);
612         $("#server_comm").removeClass("error success").addClass("error");
613     }
614 }
615
616 /* maintains a queue of ajax queries, so I'm sure they all execute in the same
617  * order they were defined */
618 var AjaxMgr = {
619     _queue: [],
620
621     running: false,
622
623     add: function(query) {
624         this._queue.push(query);
625         if (this._queue.length > 1) {
626             return;
627         } else {
628             this._runQuery(query);
629         }
630     },
631
632     _runQuery: function(query) {
633         var self = this;
634         $('#api_frame').one("load", function() {
635             self.running = false;
636             self._reqEnd();
637             if (query.throbberid) {
638                 $("#" + query.throbberid).css("visibility", "hidden");
639             }
640             if (typeof (query.oncomplete) == "function") {
641                 var body = null;
642                 try {
643                     if (this.contentDocument) {
644                         body = this.contentDocument.body;
645                     } else if (this.contentWindow) {
646                         body = this.contentWindow.document.body;
647                     } else {
648                         body = document.frames[this.id].document.body;
649                     }
650                 } catch (e) {}
651                     if (body) {
652                         query.oncomplete(body.innerHTML);
653                     } else {
654                         query.oncomplete(null);
655                     }
656             }
657         });
658         query.form.attr("action", "api.php");
659         query.form.attr("target", "api_frame");
660         query.form.attr("method", "post");
661         this.running = true;
662         query.form.get(0).submit();
663         if (query.throbberid) {
664             $("#" + query.throbberid).css("visibility", "visible");
665         }
666         if (typeof (query.onsend) == "function") {
667             query.onsend();
668         }
669     },
670
671     _reqEnd: function() {
672         this._queue.shift();
673         if (this._queue.length > 0) {
674             this._reqEnd(this._queue[0]);
675         }
676     }
677 }
678
679 var pwdMgr = {
680
681     init: function () {
682         $("#login_form").submit(this.submit);
683         $("#user").focus().select();
684     },
685
686     reset: function() {
687         this.commError ("");
688         $("#login_area").show();
689         $("#password").val("");
690         $("#user").val(sypSettings.loggedUser).focus().select();
691     },
692
693     submit: function () {
694         try {
695             pwdMgr.commError("");
696             var req = {
697                 form:  $("#login_form"),
698                 throbberid: "pwd_throbber",
699                 onsend: function() {
700                     $("#login_error").hide();
701
702                     // we need a timeout; otherwise those fields will not be submitted
703                     window.setTimeout(function() { 
704                             // removes focus from #password before disabling it. Otherwise, opera
705                             // prevents re-focusing it after re-enabling it.
706                             $("#user, #password").blur(); 
707                             $("#login_submit, #user, #password").attr("disabled", "disabled");
708                     }, 0)
709                 },
710                 oncomplete: OpenLayers.Function.bind(pwdMgr.ajaxReply, pwdMgr)
711             };
712             AjaxMgr.add(req);
713         } catch(e) {}
714         return false;
715     },
716
717     ajaxReply: function (data) {
718         // here, we need a timeout because onsend timeout sometimes has not been triggered yet
719         window.setTimeout(function() {
720             $("#login_submit, #user, #password").removeAttr("disabled");
721         }, 0);
722
723         if (!data) {
724             this.commError(SypStrings.ServerError);
725             $("#login_error").show();
726             window.setTimeout(function() {
727                     $("#user").focus().select();
728             }, 0);
729             return;
730         }
731
732         var xml = new OpenLayers.Format.XML().read(data);
733         if (!xml.documentElement) {
734             this.commError(SypStrings.UnconsistentError);
735             $("#login_error").show();
736             window.setTimeout(function() {
737                     $("#user").focus().select();
738             }, 0);
739         }
740
741         switch (xml.documentElement.nodeName.toLowerCase()) {
742             case "error":
743                 switch (xml.documentElement.getAttribute("reason")) {
744                     case "server":
745                         this.commError(SypStrings.ServerError);
746                     break;
747                     case "unauthorized":
748                         this.commError(SypStrings.UnauthorizedError);
749                     break;
750                     case "request":
751                         this.commError(SypStrings.RequestError);
752                     break;
753                     default:
754                         this.commError(SypStrings.UnconsistentError);
755                     break;
756                 }
757                 $("#login_error").show();
758                 window.setTimeout(function() {
759                         $("#user").focus().select();
760                 }, 0);
761             break;
762             case "success":
763                 $("#login_area").hide();
764
765                 user = $(xml).find("USER,user").text();
766                 sypSettings.loggedUser = user;
767
768                 if (sypSettings.loggedUser == "admin")  {
769                     userMgr.init();
770                 }
771
772                 if (Admin.selFeatureControl) {
773                     Admin.selFeatureControl.destroy();
774                 }
775                 if (Admin.moveFeatureControl) {
776                     Admin.moveFeatureControl.destroy();
777                 }
778                 if (Admin.addFeatureControl) {
779                     Admin.addFeatureControl.destroy();
780                 }
781                 if (Admin.dataLayer) {
782                     Admin.dataLayer.destroy();
783                 }
784
785                 Admin.dataLayer = Admin.createDataLayer(user);
786                 Admin.map.addLayer(Admin.dataLayer);
787                 Admin.reset();
788
789             break;
790             default:
791                 this.commError(SypStrings.UnconsistentError);
792             break;
793         }
794     },
795
796     commError: function (message) {
797         $("#login_error").text(message);
798         if (message) {
799             $("#login_error").show();
800         } else {
801             $("#login_error").hide();
802         }
803     }
804 }
805
806 var userMgr = {
807     _adduserDisplayed: false,
808     _changepassDisplayed: false,
809
810     init: function() {
811         $("#user_close").unbind("click").click(function () {
812             userMgr.close()
813         });
814
815         $("#change_pass").unbind("click").click(function() {
816             userMgr.toggleChangePass();
817             return false;
818         });
819         $("#changepass").unbind("submit").submit(function() {
820             try {
821                 userMgr.changepass();
822             } catch(e) {}
823             return false;
824         });
825
826         if (sypSettings.loggedUser != "admin") {
827             return;
828         }
829
830         $("#add_user").show();
831         $("#add_user").unbind("click").click(function () {
832             userMgr.toggleAddUser();
833             return false;
834         });
835         $("#newuser").unbind("submit").submit(function() {
836             try {
837                 userMgr.add();
838             } catch(e) {}
839             return false;
840         });
841
842     },
843
844     disableForms: function() {
845         $("#newuser_name, #newuser_password, #newuser_password_confirm, #newuser_submit").attr("disabled", "disabled");
846         $("#pass_current, #pass_new, #pass_new_confirm, #pass_submit").attr("disabled", "disabled");
847     },
848
849     enableForms: function() {
850         $("#newuser_name, #newuser_password, #newuser_password_confirm, #newuser_submit").removeAttr("disabled");
851         $("#pass_current, #pass_new, #pass_new_confirm, #pass_submit").removeAttr("disabled");
852     },
853
854     resetForms: function() {
855         $("#newuser_name, #newuser_password, #newuser_password_confirm").val("");
856         $("#pass_current, #pass_new, #pass_new_confirm").val("");
857     },
858
859     uninit: function() {
860         this.close();
861         $("#add_user").unbind("click");
862         $("#add_user").hide();
863         $("#change_pass").unbind("click");
864         $("#user_close").unbind("click");
865         $("#newuser").unbind("submit");
866         $("#changepass").unbind("submit");
867     },
868
869     close: function() {
870         this.closeChangePass();
871         this.closeAddUser();
872     },
873
874     toggleChangePass: function() {
875         if (this._changepassDisplayed) {
876             this.closeChangePass();
877         } else {
878             this.showChangePass();
879         }
880     },
881
882     showChangePass: function() {
883         if (!Admin.cancelCurrentFeature()) {
884             return;
885         }
886         this.closeAddUser();
887
888         $(document).unbind("keydown").keydown(function(e) { 
889             if (e.keyCode == 27) {
890                 userMgr.closeChangePass()
891                 e.preventDefault();
892             }
893         });
894
895         this.resetForms();
896         this.enableForms();
897         $("#user_area, #changepass").show();
898         this.commError("");
899
900         // XXX: setTimeout needed because otherwise, map becomes hidden in IE. Why ??
901         window.setTimeout(function() { 
902             $("#pass_current").focus();
903         }, 0);
904
905         this._changepassDisplayed = true;
906     },
907
908     closeChangePass: function() {
909         if (!this._changepassDisplayed) {
910             return;
911         }
912         $("#user_area, #changepass").hide();
913         $(document).unbind("keydown");
914         this._changepassDisplayed = false;
915     },
916
917     changepass: function() {
918         var newpass = $("#pass_new").val();
919         var newpass_confirm = $("#pass_new_confirm").val();
920         if (newpass != newpass_confirm) {
921             this.commError(SypStrings.userPasswordmatchError);
922             $("#pass_new").focus().select();
923             return;
924         }
925
926         if (!newpass) {
927             this.commError(SypStrings.emptyPasswordError);
928             $("#pass_new").focus().select();
929             return;
930         }
931
932         var curpass = $("#pass_current").val();
933         if (newpass == curpass) {
934             this.commError(SypStrings.changeSamePass);
935             $("#pass_new").focus().select();
936             return;
937         }
938
939         this.commError("");
940
941         AjaxMgr.add({
942             form: $("#changepass"),
943             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
944             throbberid: "user_throbber",
945             onsend: function() { 
946                 // we need a timeout; otherwise those fields will not be submitted
947                 window.setTimeout(function() {
948                     // removes focus from #password before disabling it. Otherwise, opera
949                     // prevents re-focusing it after re-enabling it.
950                     $("#pass_current, #pass_new, #pass_new_confirm").blur(); 
951                     userMgr.disableForms();
952                 }, 0);
953             }
954         });
955     },
956
957     toggleAddUser: function() {
958         if (this._adduserDisplayed) {
959             this.closeAddUser();
960         } else {
961             this.showAddUser();
962         }
963     },
964
965     showAddUser: function() {
966         if (!Admin.cancelCurrentFeature()) {
967             return;
968         }
969
970         this.closeChangePass();
971
972         $(document).unbind("keydown").keydown(function(e) { 
973             if (e.keyCode == 27) {
974                 userMgr.closeAddUser()
975                 e.preventDefault();
976             }
977         });
978
979         $("#user_area, #newuser").show();
980         this.resetForms();
981         this.enableForms();
982         this.commError("");
983
984         // XXX: setTimeout needed because otherwise, map becomes hidden in IE. Why ??
985         window.setTimeout(function() { 
986             $("#newuser_name").focus();
987         }, 0);
988
989         this._adduserDisplayed = true;
990     },
991
992     closeAddUser: function() {
993         if (!this._adduserDisplayed) {
994             return;
995         }
996         $("#user_area, #newuser").hide();
997         $(document).unbind("keydown");
998         this._adduserDisplayed = false;
999     },
1000
1001     add: function() {
1002         var newuser_name = $("#newuser_name").val();
1003         if (!newuser_name) {
1004             this.commError(SypStrings.newUserNonameError);
1005             $("#newuser_name").focus();
1006             return;
1007         }
1008
1009         var newuser_pass = $("#newuser_password").val();
1010         var newuser_pass_confirm = $("#newuser_password_confirm").val();
1011         if (newuser_pass != newuser_pass_confirm) {
1012             this.commError(SypStrings.userPasswordmatchError);
1013             $("#newuser_password").focus().select();
1014             return;
1015         }
1016
1017         if (!newuser_pass) {
1018             this.commError(SypStrings.emptyPasswordError);
1019             $("#pass_new").focus().select();
1020             return;
1021         }
1022
1023         this.commError("");
1024
1025         AjaxMgr.add({
1026             form: $("#newuser"),
1027             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this),
1028             throbberid: "user_throbber",
1029             onsend: function() { 
1030                 // we need a timeout; otherwise those fields will not be submitted
1031                 window.setTimeout(function() {
1032                     // removes focus from #password before disabling it. Otherwise, opera
1033                     // prevents re-focusing it after re-enabling it.
1034                     $("#newuser_name, #newuser_password, #newuser_password_confirm").blur(); 
1035                     userMgr.disableForms();
1036                 }, 0);
1037             }
1038         });
1039     },
1040
1041     ajaxReply: function (data) {
1042         if (!data) {
1043             // here, we need a timeout because onsend timeout sometimes has not been triggered yet
1044             var self = this;
1045             window.setTimeout(function() {
1046                 self.enableForms();
1047              }, 0);
1048             this.commError(SypStrings.ServerError);
1049             return;
1050         }
1051
1052         var xml = new OpenLayers.Format.XML().read(data);
1053         if (!xml.documentElement) {
1054             // here, we need a timeout because onsend timeout sometimes has not been triggered yet
1055             var self = this;
1056             window.setTimeout(function() {
1057                 self.enableForms();
1058              }, 0);
1059             this.commError(SypStrings.UnconsistentError);
1060             return;
1061         }
1062
1063         var needFormEnabling = true;
1064         var focusEl = null;
1065
1066         switch (xml.documentElement.nodeName.toLowerCase()) {
1067             case "error":
1068                 switch (xml.documentElement.getAttribute("reason")) {
1069                     case "unauthorized":
1070                         pwdMgr.reset();
1071                         $("#cookie_warning").show();
1072                         Admin.reset();
1073                         this.uninit();
1074                     break;
1075                     case "server":
1076                         this.commError(SypStrings.ServerError);
1077                         if (this._adduserDisplayed) {
1078                             focusEl = $("#newuser_name");
1079                         } else if (this._changepassDisplayed) {
1080                             focusEl = $("#pass_current");
1081                         }
1082                     break;
1083                     case "request":
1084                         this.commError(SypStrings.RequestError);
1085                         if (this._adduserDisplayed) {
1086                             focusEl = $("#newuser_name");
1087                         } else if (this._changepassDisplayed) {
1088                             focusEl = $("#pass_current");
1089                         }
1090                     break;
1091                     case "wrongpass":
1092                         this.commError(SypStrings.changePassBadPass);
1093                         focusEl = $("#pass_current");
1094                     break;
1095                     case "newuser_exists":
1096                         this.commError(SypStrings.newUserExistsError);
1097                         focusEl = $("#newuser_name");
1098                     break;
1099                     default:
1100                         this.commError(SypStrings.UnconsistentError);
1101                         if (this._adduserDisplayed) {
1102                             focusEl = $("#newuser_name");
1103                         } else if (this._changepassDisplayed) {
1104                             focusEl = $("#pass_current");
1105                         }
1106                     break;
1107                 }
1108             break;
1109             case "success":
1110                 switch (xml.documentElement.getAttribute("request")) {
1111                     case "newuser":
1112                         this.commSuccess(SypStrings.newUserSuccess);
1113                         needFormEnabling = false;
1114                     break;
1115                     case "changepass":
1116                         this.commSuccess(SypStrings.changePassSuccess);
1117                         needFormEnabling = false;
1118                     break;
1119                     default:
1120                         this.commError(SypStrings.UnconsistentError);
1121                         focusEl = $("newuser_name");
1122                     break;
1123                 }
1124             break;
1125             default:
1126                 this.commError(SypStrings.UnconsistentError);
1127                 focusEl = $("newuser_name");
1128             break;
1129         }
1130
1131         if (needFormEnabling) {
1132             // here, we need a timeout because onsend timeout sometimes has not been triggered yet
1133             var self = this;
1134             window.setTimeout(function() {
1135                 self.enableForms();
1136                 if (focusEl) {
1137                     focusEl.select().focus();
1138                 }
1139              }, 0);
1140         } else {
1141             if (focusEl) {
1142                 focusEl.focus().select();
1143             }
1144         }
1145
1146     },
1147
1148     commSuccess: function (message) {
1149         $("#user_comm").text(message);
1150         $("#user_comm").removeClass("error success").addClass("success");
1151     },
1152
1153     commError: function (message) {
1154         $("#user_comm").text(message);
1155         $("#user_comm").removeClass("error success").addClass("error");
1156     }
1157 }
1158
1159 $(window).load(function () {
1160     // if using .ready, ie triggers an error when trying to access
1161     // document.namespaces
1162     pwdMgr.init();
1163     $("#newfeature_button").click(function () {
1164         Admin.addNewFeature();
1165     });
1166     $("#editor_close").click(function () {
1167         Admin.cancelCurrentFeature()
1168     });
1169     $("#feature_update").submit(function() {
1170         try {
1171             FeatureMgr.save(Admin.currentFeature);
1172         } catch(e) {}
1173         return false;
1174     });
1175     $("#feature_delete").submit(function() {
1176         try {
1177             FeatureMgr.del(Admin.currentFeature);
1178         } catch(e) {}
1179         return false;
1180     });
1181     $("#image_delete").click(function() {
1182             $("#img").removeAttr('src');
1183             // needs to rebuild element otherwise some browsers still
1184             // display image.
1185             $("#img").parent().html($("#img").parent().html());
1186             $("#img").parent().hide();
1187             $("#image_delete").hide();
1188             $("#image_file").parent().show();
1189     });
1190
1191     userMgr.init();
1192     Admin.init();
1193 });