]> dev.renevier.net Git - syp.git/blob - js/admin.js
0709878efef7476b55ea339c6e93b5166df4bdd3
[syp.git] / js / admin.js
1 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
2  * license. */
3
4 OpenLayers.Control.SelectDragFeature = 
5     OpenLayers.Class (OpenLayers.Control.SelectFeature, {
6
7     lastPixel: null,
8     dragFeature: null,
9
10     startPixel : null,
11     pixelTolerance : 0,
12     timeTolerance: 300,
13     dragStart: null, 
14
15     onComplete: function (feature, pixel) {},
16     onCancel: function (feature, pixel) {},
17
18     initialize: function (layers, options) {
19         var callbacks = {
20             over: this.overFeature,
21             out: this.outFeature
22         };
23         this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
24
25         OpenLayers.Control.SelectFeature.prototype.initialize.apply(this,
26                                                                     arguments);
27
28         var handlers = {
29             drag: new OpenLayers.Handler.Drag(
30                 this, OpenLayers.Util.extend({
31                     down: this.downFeature,
32                     move: this.moveFeature,
33                     up: this.upFeature,
34                     out: this.cancel,
35                     done: this.doneDragging
36                 })
37             )
38         };
39         this.handlers = OpenLayers.Util.extend(handlers, this.handlers);
40     },
41
42     doneDragging: function (pixel) {
43         var passesTimeTolerance = 
44             (new Date ()).getTime () > this.dragStart + this.timeTolerance; 
45         var xDiff = this.startPixel.x - pixel.x;
46         var yDiff = this.startPixel.y - pixel.y;
47         var passesPixelTolerance = Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2))
48                                      > this.pixelTolerance;
49         if(passesTimeTolerance && passesPixelTolerance){
50             this.onComplete(this.dragFeature, pixel);
51         } else {
52             var res = this.map.getResolution ();
53             this.dragFeature.geometry.move(res * (this.startPixel.x - pixel.x),
54                                            res * (pixel.y - this.startPixel.y));
55             this.onCancel(this.dragFeature,pixel);
56         }
57
58         this.layer.drawFeature(this.dragFeature, "select");
59     },
60
61     cancel: function () {
62         this.handlers.drag.deactivate ();
63         this.over = false;
64     },
65
66     clickFeature: function (feature) {
67         OpenLayers.Control.SelectFeature.prototype.clickFeature.apply(this,
68                                                                       arguments);
69         if (Admin.Utils.indexOf(this.layer.selectedFeatures, feature) == -1) {
70             this.dragDisable ();
71         }
72     },
73
74     upFeature: function (pixel) {
75     },
76
77     moveFeature: function (pixel) {
78         if (Admin.Utils.indexOf(this.layer.selectedFeatures,
79                                 this.dragFeature) != -1) {
80             var res = this.map.getResolution ();
81             this.dragFeature.geometry.move(res * (pixel.x - this.lastPixel.x),
82                                    res * (this.lastPixel.y - pixel.y));
83             this.layer.drawFeature(this.dragFeature, "temporary");
84             this.lastPixel = pixel;
85         }
86     },
87
88     downFeature: function (pixel) {
89         this.handlers.feature.down = pixel;
90
91         if (Admin.Utils.indexOf(this.layer.selectedFeatures,
92                                 this.dragFeature) != -1) {
93             this.dragStart = (new Date ()).getTime ();
94             this.startPixel = pixel;
95             this.lastPixel = pixel;
96         } 
97     },
98
99     deactivate: function () {
100         this.dragDisable ();
101         return OpenLayers.Control.SelectFeature.prototype.deactivate.apply(
102             this, arguments
103         );
104     },
105
106     overFeature: function (feature) {
107         if (Admin.Utils.indexOf(this.layer.selectedFeatures, feature) != -1) {
108             this.dragEnable(feature);
109         }
110         OpenLayers.Control.SelectFeature.prototype.overFeature.apply(this,
111                                                                      arguments);
112     },
113
114     outFeature: function (feature) {
115         this.dragDisable ();
116         OpenLayers.Control.SelectFeature.prototype.outFeature.apply(this,
117                                                                     arguments);
118     },
119
120     select: function (feature) {
121         this.dragEnable(feature);
122         OpenLayers.Control.SelectFeature.prototype.select.apply(this,
123                                                                 arguments);
124     },
125
126     dragDisable: function () {
127         this.over = false;
128         if(!this.handlers.drag.dragging) {
129             this.handlers.drag.deactivate ();
130             OpenLayers.Element.removeClass(
131                 this.map.viewPortDiv, this.displayClass + "Over"
132             );
133             this.dragFeature = null;
134         }
135     },
136
137     dragEnable: function (feature) {
138         if(!this.handlers.drag.dragging) {
139             this.dragFeature = feature;
140             this.handlers.drag.activate ();
141             this.over = true;
142             OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over");
143         } else {
144             if(this.dragFeature.id == feature.id) {
145                 this.over = true;
146             } else {
147                 this.over = false;
148             }
149         }
150     },
151
152     setMap: function (map) {
153         this.handlers.drag.setMap(map);
154         OpenLayers.Control.SelectFeature.prototype.setMap.apply(this, arguments);
155     },
156
157     CLASS_NAME: "OpenLayers.Control.SelectDragFeature"
158 });
159
160 var Admin = {
161     Settings: {
162         MARKER_ICON: "openlayers/img/marker-blue.png",
163         MARKER_ICON_HEIGHT: 25,
164         MARKER_SELECT_ICON: "openlayers/img/marker-green.png",
165         MARKER_SELECT_ICON_HEIGHT: 25,
166         MARKER_TEMPORARY_ICON: "openlayers/img/marker-gold.png",
167         MARKER_TEMPORARY_ICON_HEIGHT: 25
168     },
169
170     map: null,
171     baseLayer: null,
172     dataLayer: null,
173     selectControl: null,
174     clickControl: null,
175
176     init: function () {
177         this.map = new OpenLayers.Map ("map", {
178                 controls:[
179                     new OpenLayers.Control.Navigation (),
180                     new OpenLayers.Control.PanZoom ()
181                 ],
182                 projection: new OpenLayers.Projection("EPSG:900913"),
183                 displayProjection: new OpenLayers.Projection("EPSG:4326")
184          });
185
186          this.baseLayer = this.createBaseLayer ();
187          this.dataLayer = this.createDataLayer ();
188          this.map.addLayers([this.baseLayer, this.dataLayer]);
189
190          this.selectControl = this.createSelectDragControl ();
191          this.map.addControl(this.selectControl);
192          this.selectControl.activate ();
193
194          this.clickControl = this.createClickControl ();
195          this.map.addControl(this.clickControl);
196
197          var centerBounds = new OpenLayers.Bounds ();
198
199          var mapProj = this.map.getProjectionObject();
200          var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
201
202          var bottomLeft = new OpenLayers.LonLat(sypOrig[0],sypOrig[1]);
203          bottomLeft = bottomLeft.transform(sypOrigProj, mapProj);
204          var topRight = new OpenLayers.LonLat(sypOrig[2],sypOrig[3])
205          topRight = topRight.transform(sypOrigProj, mapProj);
206
207          centerBounds.extend(bottomLeft);
208          centerBounds.extend(topRight);
209          this.map.zoomToExtent(centerBounds);
210     },
211
212     createBaseLayer: function () {
213         return new OpenLayers.Layer.OSM("OSM");
214     },
215
216     createDataLayer: function () {
217         var styleMap = new OpenLayers.StyleMap (
218                         {"default": {
219                              externalGraphic: this.Settings.MARKER_ICON,
220                              graphicHeight: this.Settings.MARKER_ICON_HEIGHT
221                                                   || 32 
222                                 },
223                          "temporary": { 
224                              externalGraphic: this.Settings.MARKER_TEMPORARY_ICON,
225                              graphicHeight: this.Settings.MARKER_TEMPORARY_ICON_HEIGHT
226                                                   || 32 
227                          },
228                          "select": { 
229                              externalGraphic: this.Settings.MARKER_SELECT_ICON,
230                              graphicHeight: this.Settings.MARKER_SELECT_ICON_HEIGHT
231                                                   || 32 
232                     }});
233
234         var layer = new OpenLayers.Layer.GML("KML", "items.php", 
235            {
236             styleMap: styleMap,
237             format: OpenLayers.Format.KML, 
238             projection: this.map.displayProjection,
239             eventListeners: { scope: this,
240                 loadend: this.checkForFeatures,
241                 featureremoved: this.checkForFeatures,
242                 featureadded: this.checkForFeatures
243             }
244        });
245
246         return layer;
247     },
248
249     createSelectDragControl: function () {
250         var control = new OpenLayers.Control.SelectDragFeature(
251                 this.dataLayer, {
252                         onSelect: this.onFeatureSelect,
253                         onUnselect: this.onFeatureUnselect,
254                         onComplete: OpenLayers.Function.bind(this.onDragComplete.action,
255                                                              this.onDragComplete),
256                         toggle: true,
257                         clickout: false
258                                });
259         return control;
260     },
261
262     checkForFeatures: function () {
263         var features = this.dataLayer.features;
264         if (features.length == 0) {
265             $("#modify_howto").css("visibility", "hidden");
266         } else {
267             $("#modify_howto").css("visibility", "visible");
268         }
269     },
270
271     createClickControl: function () {
272         var control = new OpenLayers.Control ();
273         var handler = new OpenLayers.Handler.Click(control, {});
274         control.handler = handler;
275         return control;
276     },
277
278     addMarkerNewImage: function (imgurl) {
279         return function (evt) {
280             FeatureMgr.itemDeleter.lock(imgurl);
281             var pos = this.map.getLonLatFromViewPortPx(evt.xy);
282             var point = new OpenLayers.Geometry.Point(pos.lon, pos.lat);
283             var desc = '<img src="' + imgurl + '">';
284             var feature = new OpenLayers.Feature.Vector(point, {
285                                                                name: '', 
286                                                                description: desc
287                                                                });
288             Admin.dataLayer.addFeatures([feature]);
289             Admin.selectControl.activate ();
290
291             // When adding a feature and then selecting it, we set render style
292             // to "default" and right after, we set it to "select". When
293             // rendering backend is SVG, that triggers a modification of href
294             // attribute right after having inserted image with a different
295             // href. Unfortunately, webkit does not like that (see
296             // webkit#26392). That's why we need to wrap selection in a
297             // timeout.
298             var renderer = Admin.dataLayer.renderer;
299             if (renderer && renderer.CLASS_NAME == "OpenLayers.Renderer.SVG") {
300                 window.setTimeout(function () { 
301                     Admin.selectControl.select(feature);
302                     Admin.saveFeature(feature);
303                 } ,0);
304             } else {
305                 Admin.selectControl.select(feature);
306                 Admin.saveFeature(feature);
307             }
308             $("#addphoto_button").val("ajouter une autre image");
309         }
310     },
311
312     onFeatureSelect: function (feature) {
313         Admin.closeNewimage();
314
315         $("#img").attr('src', '');
316         $("#editor").show();
317         $("#features_success").css("visibility", "hidden");
318
319         // we use the real onclick method otherwise: jquery method would not
320         // execute because of input.change method
321         $("#deletephoto_button").get(0).onclick = function () {
322             Admin.clearChangeTimeouts();
323             var imgurl = $("#img").attr("src");
324             Admin.deleteFeature(feature, imgurl);
325         };
326
327         $("#title_input").val(feature.attributes.name);
328
329         var fullDesc = $(feature.attributes.description).parent();
330         $("#desc_input").val(fullDesc.find('p').text());
331         $("#img").attr('src', fullDesc.find('img').attr('src'));
332
333         $(".input").each(function () {
334             this.curVal = this.value;
335         });
336
337          // Change event happens if an input has change if we leave that field.
338          // But we want data to be saved even if user does not blur input field
339          // (for example, he types something in the box and then does not touch
340          // it's computer). So, every 60 seconds, we check if value of input
341          // has changed. More precisely, every 60 seconds, we wait 3 seconds to
342          // see if input value is still changing and if not, it probably means
343          // user is not modifying data anymore. In that case, we save.
344
345         $(".input").focus(function (evt) {
346             var self = this;
347             this.curVal = this.value;
348             this.checkTimer = window.setInterval(function () {
349                  if (self.value != self.curVal) {
350                     $("#features_success").css("visibility", "hidden");
351                     var newVal = self.value;
352                      this.saveTimeout = window.setTimeout(function () {
353                          if (self.value == newVal) {
354                             self.curVal = self.value;
355                             Admin.saveFeature(feature);
356                          }
357                      }, 3 * 1000);
358                  }
359             }, 6 * 1000);
360         })
361
362         $(".input").blur(function (evt) {
363             if (this.checkTimer) {
364                 window.clearInterval(this.checkTimer);
365                 this.checkTimer = null;
366             }
367             if (this.saveTimeout) {
368                 window.clearTimeout(this.saveTimeout);
369                 this.saveTimeout = null;
370             }
371         });
372
373         $(".input").change(function (evt) {
374             if (this.curVal != this.value) {
375                 $("#features_success").css("visibility", "hidden");
376                 this.curVal = this.value;
377                 Admin.saveFeature(feature);
378             }
379         });
380
381         $("#title_input").blur()
382         $("#title_input").focus()
383         $("#title_input").select()
384
385         $("#dragdrop_howto").css("visibility", "visible");
386     },
387
388     onFeatureUnselect: function (feature) {
389         Admin.closeEditor();
390         $("#features_connect_error").hide();
391         $("#deletephoto_button").get(0).onclick = null;
392
393         // if user unselects feature, save modifications without waiting
394         var needsSaving = Admin.onDragComplete.timeout ? true: false;
395         $(".input").each(function () {
396             if (this.value != this.curVal) {
397                 needsSaving = true;
398             }
399             if (this.checkTimer) {
400                 window.clearInterval(this.checkTimer);
401                 this.checkTimer = null;
402             }
403             if (this.saveTimeout) {
404                 window.clearTimeout(this.saveTimeout);
405                 this.saveTimeout = null;
406             }
407         });
408
409         if (needsSaving) {
410             $("#features_success").css("visibility", "hidden");
411             Admin.saveFeature(feature);
412         }
413
414         window.clearTimeout(Admin.onDragComplete.timeout);
415         Admin.onDragComplete.timeout = null;
416     },
417
418     addNewFeature: function () {
419         this.selectControl.unselectAll();
420         $("#features_success").css("visibility", "hidden");
421         $("#features_connect_error").hide();
422         $("#addphoto_button").attr("disabled", "disabled");
423         $("#newimage").show();
424         $("#file_form").show();
425         $("#file_form").get(0).reset();
426         $("#newimage_input").change(function () {
427             $("#newimage_error").hide();
428             if (OpenLayers.Util.getBrowserName() == "msie") {
429                 if ($("#file_form").find('input[type="submit"]').length == 0) {
430                     $("#file_form").append(
431                         '<div class="center" style="margin-top: 15px">' + 
432                         '<input type="submit" class="center"><div>');
433                     $('#file_form > div > input[type="submit"]').focus();
434                     $("#file_form").one("submit", function () {
435                         $("#file_form").find('input[type="submit"]').
436                                         parent().remove();
437                         $("#newimage_throbber").css("visibility", "visible");
438                     });
439                 }
440             } else {
441                 $("#file_form").submit();
442                 $("#newimage_throbber").css("visibility", "visible");
443             }
444             $("#fileframe").one("load", FeatureMgr.fileFrameLoad);
445         });
446         // works in webkit and in ie
447         // XXX: we want to call
448         // the real click method of newimage_input, not jquery click method
449         // because jquery click method prevents change listener to be called
450         // click event is running.
451         if (OpenLayers.Util.getBrowserName() != "msie")  {
452             // XXX: in ie, it prevents submiting form
453             $("#newimage_input").get(0).click();
454         }
455         // works in opera
456         $("#newimage_input").focus(); 
457     },
458
459     closeNewimage: function () {
460         if ($("#newimage").css("display") == "none") {
461             return;
462         }
463         $("#newimage_input").unbind('change');
464         $("#fileframe").unbind('load');
465         $("#addphoto_button").removeAttr("disabled");
466         $("#newimage_error").hide();
467         $("#newimage_throbber").css("visibility", "hidden").show();
468         $("#newimage_input").val('');
469         FeatureMgr.itemDeleter.add($("#newimage_preview").attr("src"));
470         $("#newimage").hide();
471         $("#newimage_preview").removeAttr("src");
472         $("#newimage_preview").hide();
473         $("#modify_howto").css("visibility", "visible");
474         $("#newimage_warn").hide();
475         this.clickControl.handler.callbacks.click = null;
476         this.clickControl.deactivate();
477         this.selectControl.activate();
478     },
479
480     closeEditor: function () {
481         $("#editor").hide();
482         $(".input").unbind('change');
483         $(".input").unbind('focus');
484         $(".input").unbind('blur');
485         $("#dragdrop_howto").css("visibility", "hidden");
486     },
487
488     clearChangeTimeouts: function () {
489         $(".input").each(function (){
490             if (this.checkTimer) {
491                 window.clearInterval(this.checkTimer);
492                 this.checkTimer = null;
493             }
494             if (this.saveTimeout) {
495                 window.clearTimeout(this.saveTimeout);
496                 this.saveTimeout = null;
497             }
498         });
499     },
500
501     onDragComplete: {
502         timeout: null,
503         // we wait 3 seconds before saving in case user drags marker again
504         action: function (feature, pixel) {
505             if (this.timeout) {
506                 window.clearTimeout(this.timeout);
507             }
508             var self = this;
509             this.timeout = window.setTimeout(function () {
510                 self.timeout = null;
511                 Admin.saveFeature(feature);
512             }, 3000);
513         }
514     },
515
516     saveFeature: function (feature) {
517         var imgurl = $("#img").attr("src");
518         var title = $("#title_input").val();
519         var description = $("#desc_input").val();
520
521         feature.attributes.name = this.Utils.escapeHTML(title);
522         feature.attributes.description = "<p>" + 
523                                           this.Utils.escapeHTML(description) +
524                                           "</p>" +
525                                           "<img class=\"" +
526                                           $("#img").attr("class") +
527                                           "\"" +
528                                           " src=\"" + imgurl + "\">";
529
530         var x = feature.geometry.x;
531         var y = feature.geometry.y;
532
533         var mapProj = feature.layer.map.getProjectionObject();
534         var lonlat = new OpenLayers.LonLat(x, y).
535                              transform(mapProj,
536                                        new OpenLayers.Projection("EPSG:4326"));
537
538         FeatureMgr.itemDeleter.unlock(imgurl);
539         FeatureMgr.saveFeature(feature, imgurl, title, description, lonlat);
540     },
541
542     deleteFeature: function (feature, imgurl) {
543         Admin.dataLayer.destroyFeatures([feature]);
544         FeatureMgr.deleteFeature(imgurl);
545     },
546
547     reloadLayer: function (layer) {
548         layer.destroyFeatures();
549         layer.loaded = false;
550         layer.loadGML();
551         this.closeEditor();
552     },
553
554     connectErrorMsg: function (textStatus) {
555         if (textStatus == undefined) {
556             textStatus = "inconnue";
557         }
558         return "Une erreur de type " +
559                 textStatus + 
560                 " est survenue lors de la connexion au serveur." + 
561                 " Veuillez réessayer ou contacter l'administrateur du site.";
562     },
563
564     Utils: {
565         escapeHTML: function (str) {
566             return str.
567              replace(/&/gm, '&amp;').
568              replace(/'/gm, '&#39;').
569              replace(/"/gm, '&quot;').
570              replace(/>/gm, '&gt;').
571              replace(/</gm, '&lt;');
572         },
573
574         startsWith: function (str, prefix) {
575             return (str.slice(0, prefix.length) == prefix);
576         },
577
578         indexOf: function (array, item) {
579             if (array.indexOf !== undefined) {
580                 return array.indexOf(item);
581             } else {
582                 return OpenLayers.Util.indexOf(array, item);
583             }
584         }
585     }
586 }
587
588 var pwdMgr = {
589
590     init: function () {
591         $("#login_form").submit(this.submit);
592         $("#user_pwd").focus().select();
593     },
594
595     submit: function () {
596         // removes focus from #user_pwd before disabling it. Otherwise, opera
597         // prevents re-focusing it after re-enabling it.
598         $("#user_pwd").blur(); 
599         $("#login_submit, #user_pwd").attr("disabled", "disabled");
600         $("#login_connect_error, #login_password_error").hide();
601         $("#pwd_throbber").css("visibility", "visible");
602         
603         var req = {
604             type: this.method,
605             url: this.action,
606             data:  { user_pwd: this.user_pwd.value },
607             success: pwdMgr.postSuccessCallback,
608             error: pwdMgr.postErrorCallback,
609             timeout: 10000
610         };
611
612         AjaxMgr.add(req);
613         return false;
614     },
615
616     postErrorCallback: function (data, textStatus) {
617         $("#pwd_throbber").css("visibility", "hidden");
618         $("#login_submit, #user_pwd").removeAttr("disabled");
619         var errorText = Admin.connectErrorMsg(textStatus);
620         $("#login_connect_error").text(errorText).show();
621         $("#user_pwd").focus().select();
622     },
623
624     postSuccessCallback: function (data) {
625         $("#pwd_throbber").css("visibility", "hidden");
626         $("#login_submit, #user_pwd").removeAttr("disabled");
627         if (data == "access allowed") {
628             $("#login_area").hide();
629         } else {
630             $("#login_password_error").show();
631             $("#user_pwd").focus().select();
632         }
633     }
634 }
635
636 var FeatureMgr = {
637     saveFeature: function (feature, imgurl, title, description, lonlat) {
638         $("#features_success").text("La sauvegarde a été réalisée avec succès");
639         var req = {
640             type: "post",
641             url: "changes.php",
642             data:  { feature_imgurl: imgurl,
643                      feature_title: title,
644                      feature_description: description,
645                      feature_lon: lonlat.lon,
646                      feature_lat: lonlat.lat
647                     },
648             success: this.featureSuccessCallback,
649             error: this.featureErrorCallback,
650             timeout: 10000
651         };
652         AjaxMgr.add(req);
653     },
654
655     deleteFeature: function (imgurl) {
656         $("#features_success").text("La suppression a été réalisée avec succès");
657         var self = this;
658
659         var req = {
660             type: "post",
661             url: "changes.php",
662             data:  { feature_delete: imgurl },
663             success: function (data) { 
664                 if (data == "request accepted") {
665                     Admin.closeEditor();
666                     self.itemDeleter.add(imgurl);
667                 }
668                 self.featureSuccessCallback(data);
669             },
670             error: this.featureErrorCallback,
671             timeout: 10000
672         };
673         AjaxMgr.add(req);
674     },
675
676     featureSuccessCallback: function (data) {
677         switch (data) {
678             case "request accepted": // do nothing: everything went fine
679                     $("#features_success").css("visibility", "visible");
680                 return;
681
682             case "access denied":
683                 $("#login_area, #login_password_error").show();
684                 break;
685
686             case "request error":
687                 alert("Le serveur a été victime d'une erreur de requête. Il s'agit probablement d'un bug dans SYP.");
688                 break;
689
690             case "feature unavailable":
691                 alert("La photo n'était pas référencée sur le serveur. Il est possible qu'elle ait été supprimée");
692                 break;
693
694                 case "server error":
695             default:
696                 var text = Admin.connectErrorMsg();
697                 $("#features_connect_error").text(text).show();
698                 break;
699         }
700         Admin.reloadLayer(Admin.dataLayer);
701     },
702
703     featureErrorCallback: function (data, textStatus) {
704         var text = Admin.connectErrorMsg(textStatus);
705         $("#features_connect_error").text(text).show();
706         Admin.reloadLayer(Admin.dataLayer);
707     },
708
709     fileFrameLoad: function () {
710         $("#newimage_throbber").hide();
711         $("#newimage_input").val('');
712
713         var doc;
714         if (this.contentDocument) {
715             var doc = this.contentDocument;
716         } else if (this.contentWindow) {
717             var doc = this.contentWindow.document;
718         } else {
719             var doc = document.frames[this.id].document;
720         }
721         var body = $(doc.body);
722
723         if (body.children().length <= 1) { // error are signaled with a simple
724                                            // string message
725             var resp = body.html();
726             if (resp == "access denied") {
727                 $("#login_area, #login_password_error").show();
728                 Admin.closeNewimage();
729             } else if (resp == "file too big") {
730                 var text = "L'image était trop grande et n'a pas été acceptée " +
731                             "par le serveur. Veuillez réduire sa taille avant " +
732                             "de l'envoyer.";
733                 $("#newimage_error").text(text).show();
734                 $("#newimage_input").focus(); 
735             } else if (resp == "not an image") {
736                 var text = "Le fichier ne semble pas être une image.";
737                 $("#newimage_error").text(text).show();
738                 $("#newimage_input").focus(); 
739             } else {
740                 var text = Admin.connectErrorMsg();
741                 $("#newimage_error").text(text).show();
742             }
743
744         } else { // when image is successfully uploaded, informations are
745                  // passed back in document body
746             var res = body.find('.res');
747             if (res.text() == "request accepted") {
748                 $("#newimage_input").unbind('change');
749                 $("#fileframe").unbind('load');
750                 $("#file_form").hide();
751
752                 var imgurl = body.find('.infos > .imgurl').text();
753                 $("#newimage_preview").attr("src", imgurl);
754                 $("#newimage_preview").css("display", "block");
755                 var text = "Pour valider l'ajout de cette image, vous devez la " +
756                            " positionner sur la carte. Cliquez sur la carte pour " +
757                            "positionner le marqueur.";
758                 $("#newimage_warn").text(text).show();
759
760                 var clickControl = Admin.clickControl;
761                 clickControl.handler.callbacks.click = 
762                                             Admin.addMarkerNewImage(imgurl);
763                 clickControl.activate();
764
765                 Admin.selectControl.deactivate();
766                 $("#modify_howto").css("visibility", "hidden");
767             } else {
768                 var text = Admin.connectErrorMsg();
769                 $("#newimage_error").text(text).show();
770             }
771         }
772     },
773
774     itemDeleter: {
775         _items: [],
776         _locks: [],
777
778         _pushUnique: function (arr, item) {
779             if (Admin.Utils.indexOf(arr, item) == -1) {
780                 arr.push(item);
781             }
782         },
783
784         add: function (imgurl) {
785             if (!imgurl) {
786                 return;
787             }
788             if (Admin.Utils.indexOf(this._locks, imgurl) == -1) {
789                 this._pushUnique(this._items, imgurl);
790                 var brName = OpenLayers.Util.getBrowserName();
791                 // unload event does not work in opera, and webkit does not
792                 // support xmlHttpRequests in unload, so we just don't buffer
793                 // those requests.
794                 if (brName == "opera" || brName == "safari") { 
795                     this.flush();
796                 }
797             }
798         },
799
800         lock: function (imgurl) {
801             this._pushUnique(this._locks, imgurl);
802         },
803
804         unlock: function (imgurl) {
805             var idx = Admin.Utils.indexOf(this._locks, imgurl);
806             while (idx != -1) {
807                 this._locks.splice(idx, 1);
808                 idx = Admin.Utils.indexOf(this._locks, imgurl);
809             }
810         },
811
812         flush: function () {
813             if (this._items.length == 0) {
814                 return;
815             }
816             var i = 0;
817             var data = {};
818             for (var i = 0; i < this._items.length; i++) {
819                 data["imgurl_delete_" + i] = this._items[i];
820             }
821             var req = {
822                 type: "post",
823                 url: "changes.php",
824                 data:  data,
825                 success: function (data) { 
826                 },
827                 error: function (data) {
828                 }
829             };
830             AjaxMgr.add(req);
831             this._items = [];
832         }
833     }
834 }
835
836 /* maintains a queue of ajax queries, so I'm sure they all execute in the same
837  * order they were defined */
838 var AjaxMgr = {
839     _queue: [],
840
841     add: function(query) {
842         this._queue.push(query);
843         if (this._queue.length > 1) {
844             return;
845         } else {
846             this._runQuery(query);
847         }
848     },
849
850     _runQuery: function(query) {
851         var self = this;
852         $.ajax({
853             type: query.type,
854             url: query.url,
855             data: query.data,
856             success: function(data) {
857                 self._reqEnd();
858                 query.success.call(query, data);
859             },
860             error: function(data, textStatus) {
861                 self._reqEnd();
862                 query.error.call(query, data, textStatus);
863             },
864             timeout: query.timeout
865         });
866     },
867
868     _reqEnd: function() {
869         this._queue.shift();
870         if (this._queue.length > 0) {
871             this._reqEnd(this._queue[0]);
872         }
873     }
874 }
875
876 $(window).load(function () {
877     // if using .ready, ie triggers an error when trying to access
878     // document.namespaces
879     pwdMgr.init();
880     $("#newimage_close").click(function () {
881         Admin.closeNewimage();
882     });
883     $("#addphoto_button").click(function () {
884         Admin.addNewFeature();
885     });
886     Admin.init();
887 });
888 $(window).unload(function () {
889     FeatureMgr.itemDeleter.add($("#newimage_preview").attr("src"));
890     FeatureMgr.itemDeleter.flush();
891 });