]> dev.renevier.net Git - syp.git/blob - js/admin.js
removes dead code
[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     Settings: {
59         MARKER_ICON: "openlayers/img/marker-blue.png",
60         MARKER_ICON_HEIGHT: 25,
61         MARKER_SELECT_ICON: "openlayers/img/marker-green.png",
62         MARKER_SELECT_ICON_HEIGHT: 25,
63         MARKER_TEMPORARY_ICON: "openlayers/img/marker-gold.png",
64         MARKER_TEMPORARY_ICON_HEIGHT: 25
65     },
66
67     map: null,
68     baseLayer: null,
69     dataLayer: null,
70     selFeatureControl: null,
71     moveFeatureControl: null,
72     addFeatureControl: null,
73
74     currentFeature: null,
75     currentFeatureLocation: null,
76
77     init: function () {
78         this.map = new OpenLayers.Map ("map", {
79                 controls:[
80                     new OpenLayers.Control.Navigation (),
81                     new OpenLayers.Control.PanZoom ()
82                 ],
83                 projection: new OpenLayers.Projection("EPSG:900913"),
84                 displayProjection: new OpenLayers.Projection("EPSG:4326")
85          });
86
87          this.baseLayer = this.createBaseLayer ();
88          this.dataLayer = this.createDataLayer ();
89          this.map.addLayers([this.baseLayer, this.dataLayer]);
90
91          // controls
92          this.selFeatureControl = this.createSelectFeatureControl();
93          this.map.addControl(this.selFeatureControl);
94          this.moveFeatureControl = this.createMoveFeatureControl();
95          this.map.addControl(this.moveFeatureControl);
96          this.addFeatureControl = this.createNewfeatureControl();
97          this.map.addControl(this.addFeatureControl);
98
99          // position
100          var centerBounds = new OpenLayers.Bounds();
101
102          var mapProj = this.map.getProjectionObject();
103          var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
104
105          var bottomLeft = new OpenLayers.LonLat(sypOrig[0],sypOrig[1]);
106          bottomLeft = bottomLeft.transform(sypOrigProj, mapProj);
107          var topRight = new OpenLayers.LonLat(sypOrig[2],sypOrig[3])
108          topRight = topRight.transform(sypOrigProj, mapProj);
109
110          centerBounds.extend(bottomLeft);
111          centerBounds.extend(topRight);
112
113          // at that moment, ie does not know size of the map, we need to update
114          // manually
115          this.map.updateSize();
116          this.map.zoomToExtent(centerBounds);
117
118          this.reset();
119     },
120
121     reset: function() {
122         this.addFeatureControl.deactivate();
123         this.moveFeatureControl.deactivate();
124         this.selFeatureControl.activate();
125         this.checkForFeatures();
126         $("#newfeature_button").show().val(SypStrings.AddItem);
127         $("#newfeature_button").unbind("click").click(function () {
128             Admin.addNewFeature();
129         });
130     },
131
132     createBaseLayer: function () {
133         return new OpenLayers.Layer.OSM("OSM");
134     },
135
136     createDataLayer: function () {
137         var styleMap = new OpenLayers.StyleMap (
138                         {"default": {
139                              externalGraphic: this.Settings.MARKER_ICON,
140                              graphicHeight: this.Settings.MARKER_ICON_HEIGHT
141                                                   || 32 
142                                 },
143                          "temporary": { 
144                              externalGraphic: this.Settings.MARKER_TEMPORARY_ICON,
145                              graphicHeight: this.Settings.MARKER_TEMPORARY_ICON_HEIGHT
146                                                   || 32 
147                          },
148                          "select": { 
149                              externalGraphic: this.Settings.MARKER_SELECT_ICON,
150                              graphicHeight: this.Settings.MARKER_SELECT_ICON_HEIGHT
151                                                   || 32 
152                     }});
153
154         var layer = new OpenLayers.Layer.GML("KML", "items.php", 
155            {
156             styleMap: styleMap,
157             format: OpenLayers.Format.KML, 
158             projection: this.map.displayProjection,
159             eventListeners: { scope: this,
160                 loadend: this.checkForFeatures
161             }
162        });
163
164         return layer;
165     },
166
167     createMoveFeatureControl: function () {
168         var control = new OpenLayers.Control.SypDragFeature(
169                 this.dataLayer, {
170                          });
171         return control;
172     },
173
174     createSelectFeatureControl: function () {
175         var control = new OpenLayers.Control.SelectFeature(
176                 this.dataLayer, {
177                         onSelect: OpenLayers.Function.bind(this.onFeatureSelect, this)
178                          });
179         return control;
180     },
181
182     createNewfeatureControl: function () {
183         var control = new OpenLayers.Control ();
184         var handler = new OpenLayers.Handler.Click(control, {
185                 'click': OpenLayers.Function.bind(FeatureMgr.add, FeatureMgr)
186             });
187         control.handler = handler;
188         return control;
189     },
190
191     onFeatureSelect: function (feature) {
192         this.showEditor(feature);
193         FeatureMgr.reset();
194         this.selFeatureControl.deactivate();
195         this.moveFeatureControl.activate();
196     },
197
198     closeEditor: function() {
199         if (this.currentFeature && this.currentFeature.layer) {
200             this.selFeatureControl.unselect(this.currentFeature);
201         }
202         this.currentFeature = null;
203         this.currentFeatureLocation = null;
204         $("#img").removeAttr('src');
205         $("#img").parent().html($("#img").parent().html());
206         $("#img").parent().show();
207         $("#title, #description").val("");
208         $("#editor").hide();
209         // do it once before hidding and once after hidding to work in all cases
210         $("#title, #description").val(""); 
211         $("#image_file").parent().html($("#image_file").parent().html());
212         $(document).unbind("keydown");
213         this.checkForFeatures();
214         this.reset();
215     },
216
217     showEditor: function (feature) {
218         $("#newfeature_button").hide();
219         if (feature.fid) {
220             $("#delete").show();
221         } else {
222             $("#delete").hide();
223         }
224         $(document).unbind("keydown").keydown(function(e) { 
225             if (e.keyCode == 27) {
226                 Admin.cancelCurrentFeature()
227                 e.preventDefault();
228             }
229         });
230         this.currentFeature = feature;
231         this.currentFeatureLocation = new OpenLayers.Pixel(feature.geometry.x, feature.geometry.y);
232         $("#editor").show();
233         $("#instructions").text(SypStrings.DragDropHowto);
234         $("#title").val(feature.attributes.name);
235         var fullDesc = $(feature.attributes.description).parent();
236         $("#description").val(fullDesc.find('p').text());
237         var src = fullDesc.find('img').attr('src');
238         if (src) {
239             $("#img").parent().show();
240             $("#img").attr('src', src);
241             $("#image_file").parent().hide();
242             $("#image_delete").show();
243         } else {
244             $("#img").parent().hide();
245             $("#image_file").parent().show();
246             $("#image_delete").hide();
247         }
248         $("#title").select().focus(); 
249     },
250
251     checkForFeatures: function () {
252         if (this.dataLayer.features.length != 0) {
253             $("#instructions").text(SypStrings.SelectHowto);
254         }
255     },
256
257     addNewFeature: function () {
258         function cancel() {
259             $(document).unbind("keydown");
260             Admin.reset()
261         }
262         $(document).unbind("keydown").keydown(function(e) { 
263             if (e.keyCode == 27) {
264                 e.preventDefault();
265                 cancel();
266             }
267         });
268
269         $("#newfeature_button").val("annuler");
270         $("#newfeature_button").unbind("click").click(cancel);
271
272         $("#instructions").text(SypStrings.AddHowto);
273         this.selFeatureControl.deactivate();
274         this.addFeatureControl.activate();
275         FeatureMgr.reset();
276     },
277
278     cancelCurrentFeature: function() {
279         if (AjaxMgr.running) {
280             return;
281         }
282         var feature = this.currentFeature;
283         if (feature.fid) {
284             FeatureMgr.move (feature, this.currentFeatureLocation);
285         } else {
286             this.dataLayer.removeFeatures([feature]);
287         }
288         this.closeEditor();
289     },
290
291     reloadLayer: function (layer) {
292         layer.destroyFeatures();
293         layer.loaded = false;
294         layer.loadGML();
295     },
296
297     Utils: {
298         escapeHTML: function (str) {
299             if (!str) {
300                 return "";
301             }
302             return str.
303              replace(/&/gm, '&').
304              replace(/'/gm, ''').
305              replace(/"/gm, '"').
306              replace(/>/gm, '>').
307              replace(/</gm, '&lt;');
308         },
309
310         startsWith: function (str, prefix) {
311             return (str.slice(0, prefix.length) == prefix);
312         },
313
314         indexOf: function (array, item) {
315             if (array.indexOf !== undefined) {
316                 return array.indexOf(item);
317             } else {
318                 return OpenLayers.Util.indexOf(array, item);
319             }
320         }
321     }
322 }
323
324 var FeatureMgr = {
325     reset: function() {
326         this.commError("");
327     },
328
329     add: function(evt) {
330         var map = Admin.map;
331         var pos = map.getLonLatFromViewPortPx(evt.xy);
332         feature = this.update (null, pos, "", "", "");
333         Admin.addFeatureControl.deactivate();
334         Admin.selFeatureControl.select(feature);
335     },
336
337     move: function (feature, aLocation) {
338         if (!feature || !aLocation) {
339             return;
340         }
341         var curLoc = feature.geometry;
342         feature.geometry.move(aLocation.x - curLoc.x, aLocation.y - curLoc.y);
343         feature.layer.drawFeature(feature); 
344     },
345
346     update: function(feature, lonlat, imgurl, title, description) {
347         var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
348         if (!feature) {
349             feature = new OpenLayers.Feature.Vector(point);
350             Admin.dataLayer.addFeatures([feature]);
351         } else {
352             this.move (feature, point);
353         }
354         feature.attributes.name = title;
355         feature.attributes.description = "<p>" + Admin.Utils.escapeHTML(description) + "</p>"
356                                 + "<img src=\"" + imgurl + "\">"
357         return feature;
358     },
359
360     del: function (feature) {
361         var form = $("#feature_delete");
362         form.find('input[name="fid"]').val(feature.fid);
363         AjaxMgr.add({
364             form: form,
365             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this)
366         });
367     },
368
369     save: function (feature) {
370         var x = feature.geometry.x;
371         var y = feature.geometry.y;
372
373         var mapProj = feature.layer.map.getProjectionObject();
374         var lonlat = new OpenLayers.LonLat(x, y).
375                                     transform(mapProj,
376                                               new OpenLayers.Projection("EPSG:4326"));
377         var form = $("#feature_update");
378         form.find('input[name="lon"]').val(lonlat.lon);
379         form.find('input[name="lat"]').val(lonlat.lat);
380         form.find('input[name="fid"]').val(feature.fid);
381         form.find('input[name="keep_img"]').val(
382             $("#img").attr("src") ? "yes": "no"
383         );
384
385         if (feature.fid) {
386             form.find('input[name="request"]').val("update");
387         } else {
388             form.find('input[name="request"]').val("add");
389         }
390         AjaxMgr.add({
391             form: form,
392             oncomplete: OpenLayers.Function.bind(this.ajaxReply, this)
393         });
394     },
395
396     ajaxReply: function (data) {
397         if (!data) {
398             this.commError(SypStrings.ServerError);
399             return;
400         }
401
402         var xml = new OpenLayers.Format.XML().read(data);
403
404         switch (xml.documentElement.nodeName.toLowerCase()) {
405             case "error":
406                 switch (xml.documentElement.getAttribute("reason")) {
407                     case "unauthorized":
408                         $("#login_area").show();
409                         this.reset();
410                         Admin.reset();
411                     break;
412                     case "server":
413                         this.commError(SypStrings.ServerError);
414                         $("title").focus();
415                     break;
416                     case "unreferenced":
417                         this.commError(SypStrings.UnreferencedError);
418                         Admin.reloadLayer(Admin.dataLayer);
419                         Admin.closeEditor();
420                     break;
421                     case "nochange":
422                         this.commError(SypStrings.NochangeError);
423                         Admin.closeEditor();
424                     break;
425                     case "request":
426                         this.commError(SypStrings.RequestError);
427                         $("title").focus();
428                     break;
429                     case "toobig":
430                         this.commError(SypStrings.ToobigError);
431                         $("#image_file").parent().html($("#image_file").parent().html());
432                         $("#image_file").focus();
433                     break;
434                     case "notimage":
435                         this.commError(SypStrings.NotimageError);
436                         $("#image_file").parent().html($("#image_file").parent().html());
437                         $("#image_file").focus();
438                     break;
439                     default:
440                         this.commError(SypStrings.UnknownError);
441                         $("title").focus();
442                     break;
443                 }
444             break;
445             case "success":
446                 switch (xml.documentElement.getAttribute("request")) {
447                     case "del":
448                         this.commSuccess(SypStrings.DelSucces);
449                         var someFeature = false;
450                         var self = this;
451                         $.each($(xml).find("FEATURE,feature"), function () {
452                              someFeature = true;
453                              var id = parseFloat($(this).find("ID:first,id:first").text());
454                              if ((id === null) || isNaN (id)) {
455                                 return;;
456                              }
457                              var features = Admin.dataLayer.features;
458                              for (var idx = 0; idx < features.length; idx++) {
459                                  if (features[idx].fid == id) {
460                                      Admin.dataLayer.removeFeatures([features[idx]]);
461                                  }
462                              }
463                         });
464                         if (someFeature == false) {
465                             this.commError(SypStrings.UnconsistentError);
466                         } else {
467                             Admin.closeEditor();
468                         }
469                     break;
470                     case "update":
471                     case "add":
472                         var someFeature = false;
473                         var self = this;
474                         $.each($(xml).find("FEATURE,feature"), function () {
475                                 someFeature = true;
476                                 var id = parseFloat($(this).find("ID:first,id:first").text());
477                                 if ((id === null) || isNaN (id)) {
478                                     return;;
479                                 }
480
481                                 var lon = parseFloat($(this).find("LON:first,lon:first").text());
482                                 if ((typeof (lon) != "number") || isNaN (lon) ||
483                                         (lon < -180) || (lon > 180)) {
484                                     return;;
485                                 }
486
487                                 var lat = parseFloat($(this).find("LAT:first,lat:first").text());
488                                 if ((typeof (lat) != "number") || isNaN (lat) ||
489                                         (lat < -90) || (lat > 90)) {
490                                     return;;
491                                 }
492
493                                 var mapProj = Admin.map.getProjectionObject();
494                                 var lonlat = new OpenLayers.LonLat (lon, lat).
495                                                 transform( new OpenLayers.Projection("EPSG:4326"), mapProj);
496
497                                 var imgurl = $(this).find("IMGURL:first,imgurl:first").text();
498                                 var title = $(this).find("HEADING:first,heading:first").text();
499                                 var description = $(this).find("DESCRIPTION:first,description:first").text();
500
501                                 feature = self.update (Admin.currentFeature, lonlat, imgurl, title, description); 
502                                 feature.fid = id;
503                         });
504
505                         if (someFeature == false) {
506                             this.commError(SypStrings.UnconsistentError);
507                         } else {
508                             this.commSuccess(SypStrings.UpdateSucces);
509                             Admin.closeEditor();
510                         }
511
512                     break;
513                     default:
514                         this.commError(SypStrings.UnconsistentError);
515                    break;
516                 }
517             break;
518             default:
519                 this.commError(SypStrings.UnconsistentError);
520             break;
521         }
522     },
523
524     commSuccess: function (message) {
525         $("#server_comm").text(message);
526         $("#server_comm").removeClass().addClass("success");
527     },
528
529     commError: function (message) {
530         $("#server_comm").text(message);
531         $("#server_comm").removeClass().addClass("error");
532     }
533 }
534
535 /* maintains a queue of ajax queries, so I'm sure they all execute in the same
536  * order they were defined */
537 var AjaxMgr = {
538     _queue: [],
539
540     running: false,
541
542     add: function(query) {
543         this._queue.push(query);
544         if (this._queue.length > 1) {
545             return;
546         } else {
547             this._runQuery(query);
548         }
549     },
550
551     _runQuery: function(query) {
552         var self = this;
553         $('#api_frame').one("load", function() {
554             self.running = false;
555             self._reqEnd();
556             if (typeof (query.oncomplete) == "function") {
557                 var body = null;
558                 try {
559                     if (this.contentDocument) {
560                         body = this.contentDocument.body;
561                     } else if (this.contentWindow) {
562                         body = this.contentWindow.document.body;
563                     } else {
564                         body = document.frames[this.id].document.body;
565                     }
566                 } catch (e) {}
567                     if (body) {
568                         query.oncomplete(body.innerHTML);
569                     } else {
570                         query.oncomplete(null);
571                     }
572             }
573         });
574         query.form.attr("action", "api.php");
575         query.form.attr("target", "api_frame");
576         query.form.attr("method", "post");
577         this.running = true;
578         query.form.get(0).submit();
579         if (typeof (query.onsend) == "function") {
580             query.onsend();
581         }
582     },
583
584     _reqEnd: function() {
585         this._queue.shift();
586         if (this._queue.length > 0) {
587             this._reqEnd(this._queue[0]);
588         }
589     }
590 }
591
592 var pwdMgr = {
593
594     init: function () {
595         $("#login_form").submit(this.submit);
596         $("#password").focus().select();
597     },
598
599     reset: function() {
600         this.commError ("");
601     },
602
603     submit: function () {
604         try {
605             pwdMgr.commError("");
606             var req = {
607                 form:  $("#login_form"),
608                 onsend: function() {
609                     $("#pwd_throbber").css("visibility", "visible");
610                     $("#login_error").hide();
611
612                     // we need a timeout; otherwise those fields will not be submitted
613                     window.setTimeout(function() { 
614                             // removes focus from #password before disabling it. Otherwise, opera
615                             // prevents re-focusing it after re-enabling it.
616                             $("#password").blur(); 
617                             $("#login_submit, #password").attr("disabled", "disabled");
618                     }, 0)
619                 },
620                 oncomplete: OpenLayers.Function.bind(pwdMgr.ajaxReply, pwdMgr)
621             };
622             AjaxMgr.add(req);
623         } catch(e) {}
624         return false;
625     },
626
627     ajaxReply: function (data) {
628
629         $("#pwd_throbber").css("visibility", "hidden");
630         // here, we need a timeout because onsend timeout sometimes has not been triggered yet
631         window.setTimeout(function() {
632             $("#login_submit, #password").removeAttr("disabled");
633         }, 0);
634
635         if (!data) {
636             this.commError(SypStrings.ServerError);
637             $("#login_error").show();
638             window.setTimeout(function() {
639                     $("#password").focus().select();
640             }, 0);
641             return;
642         }
643         var xml = new OpenLayers.Format.XML().read(data);
644
645         switch (xml.documentElement.nodeName.toLowerCase()) {
646             case "error":
647                 switch (xml.documentElement.getAttribute("reason")) {
648                     case "server":
649                         this.commError(SypStrings.ServerError);
650                     break;
651                     case "unauthorized":
652                         this.commError(SypStrings.UnauthorizedError);
653                     break;
654                     case "request":
655                         this.commError(SypStrings.RequestError);
656                     break;
657                     default:
658                         this.commError(SypStrings.UnknownError);
659                     break;
660                 }
661                 $("#login_error").show();
662                 window.setTimeout(function() {
663                         $("#password").focus().select();
664                 }, 0);
665             break;
666             case "success":
667                 this.reset();
668                 $("#login_area").hide();
669             break;
670             default:
671                 this.commError(SypStrings.UnconsistentError);
672             break;
673         }
674     },
675
676     commError: function (message) {
677         $("#login_error").text(message);
678         if (message) {
679             $("#login_error").show();
680         } else {
681             $("#login_error").hide();
682         }
683     }
684 }
685
686 $(window).load(function () {
687     // if using .ready, ie triggers an error when trying to access
688     // document.namespaces
689     pwdMgr.init();
690     $("#newfeature_button").click(function () {
691         Admin.addNewFeature();
692     });
693     $("#editor_close").click(function () {
694         Admin.cancelCurrentFeature()
695     });
696     $("#feature_update").submit(function() {
697         try {
698             FeatureMgr.save(Admin.currentFeature);
699         } catch(e) {}
700         return false;
701     });
702     $("#feature_delete").submit(function() {
703         try {
704             FeatureMgr.del(Admin.currentFeature);
705         } catch(e) {}
706         return false;
707     });
708     $("#image_delete").click(function() {
709             $("#img").removeAttr('src');
710             // needs to rebuild element otherwise some browsers still
711             // display image.
712             $("#img").parent().html($("#img").parent().html());
713             $("#img").parent().hide();
714             $("#image_delete").hide();
715             $("#image_file").parent().show();
716     });
717
718     Admin.init();
719 });