]> dev.renevier.net Git - syp.git/blobdiff - js/syp.js
fixes CHANGES.txt
[syp.git] / js / syp.js
index a7586ca7d0fedc4232f1a85318f4bcc19dbba159..0f7785491f15a681bc556a81d0e089e7e39376a3 100644 (file)
--- a/js/syp.js
+++ b/js/syp.js
@@ -1,9 +1,76 @@
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
  * license. */
 
+/*
+ * With canvas rendering engine, externalgraphics are drawn by loading and
+ * Image javascript object and drawing it with drawImage once it has been
+ * loaded. If matching feature is deleted while image is loading, redraw
+ * function will be called before drawImage and therefore, feature is removed,
+ * but image is still drawn on the screen. We fix it with locks: when an image is
+ * loading, we defer redraw method.
+ */
+OpenLayers.Renderer.Canvas.prototype = OpenLayers.Util.extend({
+    needsRedraw: false,
+    imagesLoading: 0
+}, OpenLayers.Renderer.Canvas.prototype);
+OpenLayers.Renderer.Canvas.prototype.oldRedraw = OpenLayers.Renderer.Canvas.prototype.redraw;
+OpenLayers.Renderer.Canvas.prototype.redraw = function() {
+    if (this.imagesLoading > 0) {
+        this.needsRedraw = true;
+        return;
+    }
+    OpenLayers.Renderer.Canvas.prototype.oldRedraw.apply(this, arguments);
+}
+OpenLayers.Renderer.Canvas.prototype.drawExternalGraphic = function(pt, style) {
+    var img = new Image();
+    img.src = style.externalGraphic;
+
+    if(style.graphicTitle) {
+        img.title=style.graphicTitle;           
+    }
+
+    var width = style.graphicWidth || style.graphicHeight;
+    var height = style.graphicHeight || style.graphicWidth;
+    width = width ? width : style.pointRadius*2;
+    height = height ? height : style.pointRadius*2;
+    var xOffset = (style.graphicXOffset != undefined) ?
+        style.graphicXOffset : -(0.5 * width);
+   var yOffset = (style.graphicYOffset != undefined) ?
+       style.graphicYOffset : -(0.5 * height);
+   var opacity = style.graphicOpacity || style.fillOpacity;
+
+   var context = { img: img, 
+                   x: (pt[0]+xOffset), 
+                   y: (pt[1]+yOffset), 
+                   width: width, 
+                   height: height, 
+                   canvas: this.canvas };
+
+   var self = this;
+   this.imagesLoading++;
+   img.onerror = function() {
+       self.imagesLoading--;
+       if ((self.imagesLoading == 0) && (self.needsRedraw)) {
+           self.needsRedraw = false;
+           self.redraw();
+       }
+   }
+   img.onload = OpenLayers.Function.bind( function() {
+       self.imagesLoading--;
+       if ((self.imagesLoading == 0) && (self.needsRedraw)) {
+           self.needsRedraw = false;
+           self.redraw();
+       } else {
+            this.canvas.drawImage(this.img, this.x, 
+                             this.y, this.width, this.height);
+       }
+   }, context);   
+}
+
+
 OpenLayers.Control.SypAttribution = OpenLayers.Class (OpenLayers.Control.Attribution, {
     updateAttribution: function() {
-        var attributions = [SypStrings.propulsedByLink];
+        var attributions = [SypStrings.poweredByLink];
         if (this.map && this.map.layers) {
             for(var i=0, len=this.map.layers.length; i<len; i++) {
                 var layer = this.map.layers[i];
@@ -60,13 +127,23 @@ var SYP = {
     createDataLayer: function(map) {
         var defaultStyle = new OpenLayers.Style({
             externalGraphic: this.Markers.ICON,
-            graphicHeight: "${height}"
+            graphicHeight: "${height}",
+            label: "${label}",
+            fontColor: "white",
+            fontWeight: "bold"
         }, {
             context: {
                 height: function(feature) {
                     var defaultHeight = SYP.Markers.HEIGHT || 32;
                     var increase = 4 * (feature.attributes.count - 1);
                     return Math.min(defaultHeight + increase, 50);
+                },
+                label: function(feature) {
+                    var renderer = feature.layer.renderer;
+                    if (renderer.CLASS_NAME == "OpenLayers.Renderer.Canvas") {
+                        return ""; // canvas backend cannot draw text above an external Image
+                    }
+                    return (feature.attributes.count > 1) ? feature.attributes.count: "";
                 }
             }
         });
@@ -111,8 +188,8 @@ var SYP = {
         }
 
         var map = this.map;
-        if (map.getControlsByClass("OpenLayers.Control.ArgParser")[0].lat
-            == undefined) { // map center was not set in ArgParser control.
+        if (map.getControlsByClass("OpenLayers.Control.ArgParser")[0].center
+            == null) { // map center was not set in ArgParser control.
             var orig = this.Utils.mbr (this.dataLayer);
             var centerBounds = new OpenLayers.Bounds();
 
@@ -169,11 +246,36 @@ var SYP = {
         }
     },
 
+    onZoomClusterEnd: function(arg) {
+        var map = arg.object;
+        var point = new OpenLayers.Geometry.Point(this.lonlat.lon, this.lonlat.lat);
+        var center = map.getCenter();
+        for (var i = this.layer.features.length; i-->0;) {
+            var feature = this.layer.features[i];
+            if (feature.geometry.equals(point) && 
+                (feature.attributes.count == this.count)) {
+                   var self = this;
+                   window.setTimeout(function() { map.setCenter(self.lonlat, map.zoom + 1)}, 500);
+                   return;
+            }
+        }
+        SYP.selectControl.activate();
+        map.events.unregister("zoomend", this, SYP.onZoomClusterEnd);
+    },
+
     onFeatureSelect: function(feature) {
         var map = feature.layer.map;
+
         if (feature.attributes.count > 1) {
             this.unselect(feature);
             var lonlat = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y);
+            var args = {
+                lonlat: lonlat,
+                layer: feature.layer,
+                count: feature.attributes.count
+            }
+            map.events.register("zoomend", args, SYP.onZoomClusterEnd);
+            SYP.selectControl.deactivate();
             map.setCenter(lonlat, map.zoom + 1);
             return;
         }
@@ -212,8 +314,9 @@ var SYP = {
         }
         var contentHTML;
         if (feature.cluster[0].attributes.name) {
+            // escaping name is necessary because it's not enclosed in another html tag.
             contentHTML = "<h2>" +
-                          feature.cluster[0].attributes.name + 
+                          SYP.Utils.escapeHTML(feature.cluster[0].attributes.name) +
                           "</h2>" + 
                           feature.cluster[0].attributes.description;
         } else {
@@ -264,9 +367,18 @@ var SYP = {
                 }
             }
 
+            var offsetTop = this.offsetTop;
+            var offsetLeft = this.offsetLeft;
+            var par = this.offsetParent;
+            var ismsie = OpenLayers.Util.getBrowserName() == "msie";
+            while (par && !ismsie) {
+                offsetTop += par.offsetTop;
+                offsetLeft += par.offsetLeft;
+                par = par.offsetParent;
+            }
             var icon = document.getElementById('bigimg_close');
-            icon.style.top = this.offsetTop;
-            icon.style.left = this.offsetLeft + this.clientWidth - icon.clientWidth;
+            icon.style.top = offsetTop;
+            icon.style.left = offsetLeft + this.clientWidth - icon.clientWidth;
 
         };
         document.getElementById('bigimg').src = href;
@@ -384,6 +496,18 @@ var SYP = {
             }
             div.style.display = "block";
             div.appendChild(textNode);
+        },
+
+        escapeHTML: function (str) {
+            if (!str) {
+                return "";
+            }
+            return str.
+             replace(/&/gm, '&amp;').
+             replace(/'/gm, '&#39;').
+             replace(/"/gm, '&quot;').
+             replace(/>/gm, '&gt;').
+             replace(/</gm, '&lt;');
         }
     }
 };
@@ -406,45 +530,49 @@ OpenLayers.Console.userError = function(error) {
 // least as big as content. To achieve that, we need to override
 // OpenLayers.Popup.Anchored.prototype.updateSize to modify it slightly.
 OpenLayers.Popup.Anchored.prototype.updateSize = function() {
-    // determine actual render dimensions of the contents by putting its
-    // contents into a fake contentDiv (for the CSS) and then measuring it
-    var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" + 
-        this.contentDiv.innerHTML + 
-        "</div>";
-
-    var containerElement = (this.map) ? this.map.layerContainerDiv
-                                      : document.body;
-    var realSize = OpenLayers.Util.getRenderedDimensions(
-        preparedHTML, null,    {
-            displayClass: this.displayClass,
-            containerElement: containerElement
+    var self = this;
+
+    window.setTimeout(function() { // timeout added by SYP
+
+        // determine actual render dimensions of the contents by putting its
+        // contents into a fake contentDiv (for the CSS) and then measuring it
+        var preparedHTML = "<div class='" + self.contentDisplayClass+ "'>" + 
+            self.contentDiv.innerHTML + 
+            "</div>";
+
+        var containerElement = (self.map) ? self.map.layerContainerDiv
+                                          : document.body;
+        var realSize = OpenLayers.Util.getRenderedDimensions(
+            preparedHTML, null,        {
+                displayClass: self.displayClass,
+                containerElement: containerElement
+            }
+        );
+
+        /*
+         * XXX: next four lines are added by SYP!
+         */
+        if (self.contentDiv) {
+            realSize.w = Math.max (realSize.w, self.contentDiv.scrollWidth);
+            realSize.h = Math.max (realSize.h, self.contentDiv.scrollHeight);
         }
-    );
-
-    /*
-     * XXX: next four lines are added by SYP!
-     */
-    if (this.contentDiv) {
-        realSize.w = Math.max (realSize.w, this.contentDiv.scrollWidth);
-        realSize.h = Math.max (realSize.h, this.contentDiv.scrollHeight);
-    }
 
-    // is the "real" size of the div is safe to display in our map?
-    var safeSize = this.getSafeContentSize(realSize);
+        // is the "real" size of the div is safe to display in our map?
+        var safeSize = self.getSafeContentSize(realSize);
 
-    var newSize = null;
-    if (safeSize.equals(realSize)) {
-        //real size of content is small enough to fit on the map, 
-        // so we use real size.
-        newSize = realSize;
+        var newSize = null;
+        if (safeSize.equals(realSize)) {
+            //real size of content is small enough to fit on the map, 
+            // so we use real size.
+            newSize = realSize;
 
-    } else {
+        } else {
 
-        //make a new OL.Size object with the clipped dimensions 
-        // set or null if not clipped.
-        var fixedSize = new OpenLayers.Size();
-        fixedSize.w = (safeSize.w < realSize.w) ? safeSize.w : null;
-        fixedSize.h = (safeSize.h < realSize.h) ? safeSize.h : null;
+            //make a new OL.Size object with the clipped dimensions 
+            // set or null if not clipped.
+            var fixedSize = new OpenLayers.Size();
+            fixedSize.w = (safeSize.w < realSize.w) ? safeSize.w : null;
+            fixedSize.h = (safeSize.h < realSize.h) ? safeSize.h : null;
 
             if (fixedSize.w && fixedSize.h) {
                 //content is too big in both directions, so we will use 
@@ -456,7 +584,7 @@ OpenLayers.Popup.Anchored.prototype.updateSize = function() {
                 // run getRenderedDimensions() again with a fixed dimension
                 var clippedSize = OpenLayers.Util.getRenderedDimensions(
                     preparedHTML, fixedSize, {
-                        displayClass: this.contentDisplayClass,
+                        displayClass: self.contentDisplayClass,
                         containerElement: containerElement
                     }
                 );
@@ -468,7 +596,7 @@ OpenLayers.Popup.Anchored.prototype.updateSize = function() {
                 // adjust for that.
                 //
                 var currentOverflow = OpenLayers.Element.getStyle(
-                    this.contentDiv, "overflow"
+                    self.contentDiv, "overflow"
                 );
                 if ( (currentOverflow != "hidden") && 
                      (clippedSize.equals(safeSize)) ) {
@@ -480,8 +608,9 @@ OpenLayers.Popup.Anchored.prototype.updateSize = function() {
                     }
                 }
 
-                newSize = this.getSafeContentSize(clippedSize);
+                newSize = self.getSafeContentSize(clippedSize);
             }
         }                        
-        this.setSize(newSize);     
+        self.setSize(newSize);     
+    }, 0);
 }