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