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