OpenLayers OpenLayers

Ticket #1648: wfs.patch

File wfs.patch, 115.1 kB (added by tschaub, 3 months ago)

wfs protocol/formats for v1.0 and 1.1

  • tests/Format/GML/v3.html

    old new  
    198198<body> 
    199199<div id="v3/envelope.xml"><!-- 
    200200<gml:Envelope xmlns:gml="http://www.opengis.net/gml" srsName="foo"> 
    201     <gml:lowerCorner><gml:pos>1 2</gml:pos></gml:lowerCorner> 
    202     <gml:upperCorner><gml:pos>3 4</gml:pos></gml:upperCorner> 
     201    <gml:lowerCorner>1 2</gml:lowerCorner> 
     202    <gml:upperCorner>3 4</gml:upperCorner> 
    203203</gml:Envelope> 
    204204--></div> 
    205205<div id="v3/linearring.xml"><!-- 
  • tests/Protocol/WFS/v1_0_0.html

    old new  
     1<html> 
     2<head> 
     3  <script src="../../../lib/OpenLayers.js"></script> 
     4  <script type="text/javascript"> 
     5 
     6    function test_initialize(t) { 
     7        t.plan(1); 
     8 
     9        var protocol = new OpenLayers.Protocol.WFS.v1_0_0({ 
     10            format: new OpenLayers.Format.GML.v2({ 
     11                featureType: "type", 
     12                featureNS: "http://namespace.org" 
     13            }) 
     14        }); 
     15        t.ok(protocol instanceof OpenLayers.Protocol.WFS.v1_0_0, 
     16             "initialize returns instance") 
     17    } 
     18     
     19    function test_read(t) { 
     20        t.plan(2); 
     21 
     22        var url = "http://some.url.org"; 
     23        var protocol = new OpenLayers.Protocol.WFS.v1_0_0({ 
     24            url: url, 
     25            featureNS: "http://namespace.org", 
     26            featureType: "type", 
     27            format: new OpenLayers.Format.GML.v2({ 
     28                featureNS: "http://namespace.org", 
     29                featureType: "type", 
     30            }) 
     31        }); 
     32 
     33        var _POST = OpenLayers.Request.POST; 
     34         
     35        OpenLayers.Request.POST = function(obj) { 
     36            return obj; 
     37        }; 
     38         
     39        var response = protocol.read(); 
     40        var simpleReadXML = '<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" outputFormat="GML2"><wfs:Query typeName="feature:type" srsName="EPSG:4326" xmlns:feature="http://namespace.org"/></wfs:GetFeature>'; 
     41         
     42        var request = response.priv; 
     43        t.xml_eq(request.data, simpleReadXML, "read with no option"); 
     44 
     45        options = { 
     46            maxFeatures: 10, 
     47            featureType: 'type2', 
     48            srsName: 'EPSG:900913', 
     49            featureNS: 'htttp://alternative.namespace.org' 
     50        }; 
     51        var optionsReadXML = '<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" maxFeatures="10" outputFormat="GML2"><wfs:Query typeName="feature:type2" srsName="EPSG:900913" xmlns:feature="htttp://alternative.namespace.org"/></wfs:GetFeature>'; 
     52         
     53        var response = protocol.read(options); 
     54        var request = response.priv; 
     55        t.xml_eq(request.data, optionsReadXML, "read with options") 
     56        
     57        OpenLayers.Request.POST = _POST; 
     58    } 
     59     
     60    function test_update(t) { 
     61        t.plan( 4 ); 
     62 
     63        var url = "http://some.url.org"; 
     64        var protocol = new OpenLayers.Protocol.WFS.v1_0_0({ 
     65            url: url, 
     66            featureNS: "http://namespace.org", 
     67            featureType: "type", 
     68            format: new OpenLayers.Format.GML.v2({ 
     69                featureNS: "http://namespace.org", 
     70                featureType: "type", 
     71            }) 
     72        }); 
     73 
     74        var test_feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(42, 7), {has : "cheeseburger"}); 
     75        test_feature.fid = "fid.37"; 
     76        test_feature.layer = { 
     77            projection: { 
     78                getCode : function(){ 
     79                    return "EPSG:4326"; 
     80                } 
     81            } 
     82        } 
     83 
     84        options = {featureNS: "http://some.namespace.org", featureType: "type"} 
     85 
     86        var node = protocol.update(test_feature,options); 
     87        
     88        t.ok(node,"Output of update is not null"); 
     89        t.eq(node.nodeType, 1, "Output of update is a node of the proper type."); 
     90        t.eq(node.tagName, "wfs:Update", "Tagname is the appropriate one."); 
     91        t.eq(node.textContent, "the_geom42,7hascheeseburger", "The node created by update has the right textcontent."); 
     92    } 
     93     
     94    function test_delete(t) { 
     95        t.plan( 2 ); 
     96 
     97        var url = "http://some.url.org"; 
     98        var protocol = new OpenLayers.Protocol.WFS.v1_0_0({ 
     99            url: url, 
     100            featureNS: "http://namespace.org", 
     101            featureType: "type", 
     102            format: new OpenLayers.Format.GML.v2({ 
     103                featureNS: "http://namespace.org", 
     104                featureType: "type", 
     105            }) 
     106        }); 
     107 
     108        var test_feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(42, 7), {has : "cheeseburger"}); 
     109        test_feature.fid = "fid.37"; 
     110        test_feature.layer = { 
     111            projection: { 
     112                getCode : function(){ 
     113                    return "EPSG:4326"; 
     114                } 
     115            } 
     116        } 
     117 
     118        options = {featureNS: "http://some.namespace.org", featureType: "type"} 
     119        var node = protocol['delete'](test_feature, options); 
     120        
     121        t.eq(node.tagName, "wfs:Delete", "Tagname is the appropriate one."); 
     122        t.eq(node.firstChild.firstChild.attributes.fid.nodeValue, "fid.37", "The right fid is stored in an attribute as expected."); 
     123    } 
     124     
     125     
     126    //TODO: Add more commit tests 
     127    function test_commit(t){ 
     128        t.plan(2); 
     129 
     130        var url = "http://some.url.org"; 
     131        var protocol = new OpenLayers.Protocol.WFS.v1_0_0({ 
     132            url: url, 
     133            featureNS: "http://namespace.org", 
     134            featureType: "type", 
     135            format: new OpenLayers.Format.GML.v2({ 
     136                featureNS: "http://namespace.org", 
     137                featureType: "type", 
     138            }) 
     139        }); 
     140      
     141        var _POST = OpenLayers.Request.POST;         
     142        OpenLayers.Request.POST = function(object) { 
     143            return object; 
     144        }; 
     145         
     146        var featureFactory = function(attribute, id) { 
     147            var feature = new OpenLayers.Feature.Vector( 
     148                new OpenLayers.Geometry.Point(42, 7), {has: attribute} 
     149            ); 
     150            feature.layer = { 
     151                projection: { 
     152                    getCode : function(){ 
     153                        return "EPSG:4326"; 
     154                    } 
     155                } 
     156            } 
     157            feature.fid = "fid." + id;             
     158            return feature; 
     159        } 
     160         
     161        var test_feature1 = featureFactory("cheeseburger",7); 
     162         
     163        var response = protocol.commit([test_feature1]); 
     164        var request = response.priv; 
     165        var expected = '<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" outputFormat="GML2" />'; 
     166       
     167        t.xml_eq(request.data, expected, "Correct envelope XML for request data for no changed features"); 
     168 
     169        //testing getMethod 
     170        test_feature1.state = OpenLayers.State.INSERT; 
     171         
     172        var test_feature2 = featureFactory("lolcat", 8); 
     173        test_feature2.state = OpenLayers.State.UPDATE; 
     174                 
     175        var test_feature3 = featureFactory("asdf", 9); 
     176        test_feature3.state = OpenLayers.State.DELETE; 
     177         
     178        var response = protocol.commit([test_feature1, test_feature2, test_feature3]); 
     179        var request = response.priv; 
     180         
     181        expected = '<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" outputFormat="GML2" xmlns:feature="http://namespace.org"><wfs:Insert><feature:type fid="fid.7"><feature:geometry><gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">42,7</gml:coordinates></gml:Point></feature:geometry><feature:has>cheeseburger</feature:has></feature:type></wfs:Insert><wfs:Update typeName="feature:type"><wfs:Property><wfs:Name>the_geom</wfs:Name><wfs:Value><gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">42,7</gml:coordinates></gml:Point></wfs:Value></wfs:Property><wfs:Property><wfs:Name>has</wfs:Name><wfs:Value>lolcat</wfs:Value></wfs:Property><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:FeatureId fid="fid.8"/></ogc:Filter></wfs:Update><wfs:Delete typeName="feature:type"><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:FeatureId fid="fid.9"/></ogc:Filter></wfs:Delete></wfs:Transaction>'; 
     182         
     183        t.xml_eq(request.data, expected, "Got expected XML for commit with insert, update, and delete"); 
     184         
     185        OpenLayers.Request.POST = _POST; 
     186    } 
     187 
     188    function test_callback(t) { 
     189        t.plan(3) 
     190 
     191        var xmlText = '<?xml version="1.0" encoding="UTF-8"?><wfs:WFS_TransactionResponse version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://sigma.openplans.org:80/geoserver/schemas/wfs/1.0.0/WFS-transaction.xsd"><wfs:InsertResult><ogc:FeatureId fid="new0"/><ogc:FeatureId fid="new1"/><ogc:FeatureId fid="new2"/><ogc:FeatureId fid="new3"/><ogc:FeatureId fid="new4"/><ogc:FeatureId fid="new5"/></wfs:InsertResult> <wfs:TransactionResult> <wfs:Status> <wfs:SUCCESS/> </wfs:Status> </wfs:TransactionResult></wfs:WFS_TransactionResponse>'; 
     192 
     193        var url = "http://some.url.org"; 
     194        var protocol = new OpenLayers.Protocol.WFS.v1_0_0({ 
     195            url: url, 
     196            featureNS: "http://namespace.org", 
     197            featureType: "type", 
     198            format: new OpenLayers.Format.GML.v2({ 
     199                featureNS: "http://namespace.org", 
     200                featureType: "type", 
     201            }) 
     202        }); 
     203 
     204        var _POST = OpenLayers.Request.POST; 
     205        var xml = new OpenLayers.Format.XML(); 
     206        OpenLayers.Request.POST = function(obj) { 
     207            var request = {responseXML: xml.read(xmlText)}; 
     208            window.setTimeout(function() { 
     209                obj.callback.call(obj.scope, request); 
     210            }, 0.1); 
     211            t.delay_call(0.2, function() { 
     212                t.ok(cbResponse.success(), "success parsed and set on response"); 
     213                t.eq(cbResponse.insertIds,  ["new0","new1","new2","new3","new4","new5"], 
     214                     "Inserted FIDs parsed and passed through to callback"); 
     215                t.eq(cbScope.foo, "bar", "Callback called in proper scope"); 
     216            }); 
     217            return request; 
     218        }; 
     219         
     220        var cbResponse, cbScope; 
     221        var options = { 
     222            callback: function(response) { 
     223                cbResponse = response; 
     224                cbScope = this; 
     225            }, 
     226            scope: {foo: "bar"} 
     227        }; 
     228        var response = protocol.commit([], options); 
     229     
     230        OpenLayers.Request.POST = _POST; 
     231    } 
     232 
     233  </script> 
     234</head> 
     235<body> 
     236<div id="map" style="width:512px; height:256px"> </div> 
     237</body> 
     238</html> 
  • tests/list-tests.html

    old new  
    119119    <li>Protocol/HTTP.html</li> 
    120120    <li>Protocol/SQL.html</li> 
    121121    <li>Protocol/SQL/Gears.html</li> 
     122    <li>Protocol/WFS/v1_0_0.html</li> 
    122123    <li>Renderer.html</li> 
    123124    <li>Renderer/Canvas.html</li> 
    124125    <li>Renderer/Elements.html</li> 
  • lib/OpenLayers/Filter/Comparison.js

    old new  
    4646    value: null, 
    4747     
    4848    /** 
     49     * Property: matchCase 
     50     * {Boolean} Force case sensitive searches for EQUAL_TO comparisons.  The 
     51     *     Filter Encoding 1.1 specification added a matchCase attribute to 
     52     *     ogc:PropertyIsEqualTo elements.  This property will be serialized 
     53     *     with those elements only if using the v1.1.0 filter format. 
     54     *     However, when evaluating filters here, the matchCase property 
     55     *     will always be respected (for EQUAL_TO).  Default is true. 
     56     */ 
     57    matchCase: true, 
     58     
     59    /** 
    4960     * APIProperty: lowerBoundary 
    5061     * {Number} or {String} 
    5162     * lower boundary for between comparisons. In the case of a String, this 
     
    90101     * {Boolean} The filter applies. 
    91102     */ 
    92103    evaluate: function(context) { 
     104        var result = false; 
    93105        switch(this.type) { 
    94106            case OpenLayers.Filter.Comparison.EQUAL_TO: 
     107                var got = context[this.property]; 
     108                var exp = this.value; 
     109                if(!this.matchCase && 
     110                   typeof got == "string" && typeof exp == "string") { 
     111                    result = (got.toUpperCase() == exp.toUpperCase()); 
     112                } else { 
     113                    result = (got == exp); 
     114                } 
     115                break; 
     116            case OpenLayers.Filter.Comparison.NOT_EQUAL_TO: 
     117                result = (context[this.property] != this.value); 
     118                break; 
    95119            case OpenLayers.Filter.Comparison.LESS_THAN: 
     120                result = context[this.property] < this.value; 
     121                break; 
    96122            case OpenLayers.Filter.Comparison.GREATER_THAN: 
     123                result = context[this.property] > this.value; 
     124                break; 
    97125            case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO: 
     126                result = context[this.property] <= this.value; 
     127                break; 
    98128            case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO: 
    99                 return this.binaryCompare(context, this.property, this.value)
    100              
     129                result = context[this.property] >= this.value
     130                break; 
    101131            case OpenLayers.Filter.Comparison.BETWEEN: 
    102                 var result = 
    103                         context[this.property] >= this.lowerBoundary; 
    104                 result = result && 
    105                         context[this.property] <= this.upperBoundary; 
    106                 return result; 
     132                result = (context[this.property] >= this.lowerBoundary) && 
     133                    (context[this.property] <= this.upperBoundary); 
     134                break; 
    107135            case OpenLayers.Filter.Comparison.LIKE: 
    108                 var regexp = new RegExp(this.value, 
    109                                 "gi"); 
    110                 return regexp.test(context[this.property]);  
     136                var regexp = new RegExp(this.value, "gi"); 
     137                result = regexp.test(context[this.property]); 
     138                break; 
    111139        } 
     140        return result; 
    112141    }, 
    113142     
    114143    /** 
     
    191220         
    192221        return value; 
    193222    }, 
    194  
    195     /** 
    196      * Function: binaryCompare 
    197      * Compares a feature property to a rule value 
    198      *  
    199      * Parameters: 
    200      * context  - {Object} 
    201      * property - {String} or {Number} 
    202      * value    - {String} or {Number}, same as property 
    203      *  
    204      * Returns: 
    205      * {Boolean} 
    206      */ 
    207     binaryCompare: function(context, property, value) { 
    208         switch (this.type) { 
    209             case OpenLayers.Filter.Comparison.EQUAL_TO: 
    210                 return context[property] == value; 
    211             case OpenLayers.Filter.Comparison.NOT_EQUAL_TO: 
    212                 return context[property] != value; 
    213             case OpenLayers.Filter.Comparison.LESS_THAN: 
    214                 return context[property] < value; 
    215             case OpenLayers.Filter.Comparison.GREATER_THAN: 
    216                 return context[property] > value; 
    217             case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO: 
    218                 return context[property] <= value; 
    219             case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO: 
    220                 return context[property] >= value; 
    221         }       
    222     }, 
    223223     
    224224    CLASS_NAME: "OpenLayers.Filter.Comparison" 
    225225}); 
  • lib/OpenLayers/Format/GML/v3.js

    old new  
    141141            }, 
    142142            "lowerCorner": function(node, container) { 
    143143                var obj = {}; 
    144                 this.readChildNodes(node, obj); 
     144                this.readers.gml.pos.apply(this, [node, obj]); 
    145145                container.points[0] = obj.points[0]; 
    146146            }, 
    147147            "upperCorner": function(node, container) { 
    148148                var obj = {}; 
    149                 this.readChildNodes(node, obj); 
     149                this.readers.gml.pos.apply(this, [node, obj]); 
    150150                container.points[1] = obj.points[0]; 
    151151            } 
    152152        }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),             
     
    268268                return node; 
    269269            }, 
    270270            "lowerCorner": function(bounds) { 
    271                 var node = this.createElementNSPlus("gml:lowerCorner"); 
    272                 this.writeNode("pos", {x: bounds.left, y: bounds.bottom}, node); 
    273                 return node; 
     271                // only 2d for simple features profile 
     272                var pos = (this.xy) ? 
     273                    (bounds.left + " " + bounds.bottom) : 
     274                    (bounds.bottom + " " + bounds.left); 
     275                return this.createElementNSPlus("gml:lowerCorner", { 
     276                    value: pos 
     277                }); 
    274278            }, 
    275279            "upperCorner": function(bounds) { 
    276                 var node = this.createElementNSPlus("gml:upperCorner"); 
    277                 this.writeNode("pos", {x: bounds.right, y: bounds.top}, node); 
    278                 return node; 
     280                // only 2d for simple features profile 
     281                var pos = (this.xy) ? 
     282                    (bounds.right + " " + bounds.top) : 
     283                    (bounds.top + " " + bounds.right); 
     284                return this.createElementNSPlus("gml:upperCorner", { 
     285                    value: pos 
     286                }); 
    279287            } 
    280288        }, OpenLayers.Format.GML.Base.prototype.writers["gml"]), 
    281289        "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"], 
  • lib/OpenLayers/Format/GML/Base.js

    old new  
    88 */ 
    99 
    1010/** 
     11 * Though required in the full build, if the GML format is excluded, we set 
     12 * the namespace here. 
     13 */ 
     14if(!OpenLayers.Format.GML) { 
     15    OpenLayers.Format.GML = {}; 
     16} 
     17 
     18/** 
    1119 * Class: OpenLayers.Format.GML.Base 
    1220 * Superclass for GML parsers. 
    1321 * 
  • lib/OpenLayers/Format/SLD/v1.js

    old new  
    396396                ); 
    397397                // add in optional name 
    398398                if(sld.name) { 
    399                     this.writeNode(root, "Name", sld.name); 
     399                    this.writeNode("Name", sld.name, root); 
    400400                } 
    401401                // add in optional title 
    402402                if(sld.title) { 
    403                     this.writeNode(root, "Title", sld.title); 
     403                    this.writeNode("Title", sld.title, root); 
    404404                } 
    405405                // add in optional description 
    406406                if(sld.description) { 
    407                     this.writeNode(root, "Abstract", sld.description); 
     407                    this.writeNode("Abstract", sld.description, root); 
    408408                } 
    409409                // add in named layers 
    410410                for(var name in sld.namedLayers) { 
    411                     this.writeNode(root, "NamedLayer", sld.namedLayers[name]); 
     411                    this.writeNode("NamedLayer", sld.namedLayers[name], root); 
    412412                } 
    413413                return root; 
    414414            }, 
     
    427427                var node = this.createElementNSPlus("NamedLayer"); 
    428428 
    429429                // add in required name 
    430                 this.writeNode(node, "Name", layer.name); 
     430                this.writeNode("Name", layer.name, node); 
    431431 
    432432                // optional sld:LayerFeatureConstraints here 
    433433 
     
    435435                if(layer.namedStyles) { 
    436436                    for(var i=0, len=layer.namedStyles.length; i<len; ++i) { 
    437437                        this.writeNode( 
    438                             node, "NamedStyle", layer.namedStyles[i] 
     438                            "NamedStyle", layer.namedStyles[i], node 
    439439                        ); 
    440440                    } 
    441441                } 
     
    444444                if(layer.userStyles) { 
    445445                    for(var i=0, len=layer.userStyles.length; i<len; ++i) { 
    446446                        this.writeNode( 
    447                             node, "UserStyle", layer.userStyles[i] 
     447                            "UserStyle", layer.userStyles[i], node 
    448448                        ); 
    449449                    } 
    450450                } 
     
    453453            }, 
    454454            "NamedStyle": function(name) { 
    455455                var node = this.createElementNSPlus("NamedStyle"); 
    456                 this.writeNode(node, "Name", name); 
     456                this.writeNode("Name", name, node); 
    457457                return node; 
    458458            }, 
    459459            "UserStyle": function(style) { 
     
    461461 
    462462                // add in optional name 
    463463                if(style.name) { 
    464                     this.writeNode(node, "Name", style.name); 
     464                    this.writeNode("Name", style.name, node); 
    465465                } 
    466466                // add in optional title 
    467467                if(style.title) { 
    468                     this.writeNode(node, "Title", style.title); 
     468                    this.writeNode("Title", style.title, node); 
    469469                } 
    470470                // add in optional description 
    471471                if(style.description) { 
    472                     this.writeNode(node, "Abstract", style.description); 
     472                    this.writeNode("Abstract", style.description, node); 
    473473                } 
    474474                 
    475475                // add isdefault 
    476476                if(style.isDefault) { 
    477                     this.writeNode(node, "IsDefault", style.isDefault); 
     477                    this.writeNode("IsDefault", style.isDefault, node); 
    478478                } 
    479479                 
    480480                // add FeatureTypeStyles 
    481                 this.writeNode(node, "FeatureTypeStyle", style); 
     481                this.writeNode("FeatureTypeStyle", style, node); 
    482482                 
    483483                return node; 
    484484            }, 
     
    496496                 
    497497                // add in rules 
    498498                for(var i=0, len=style.rules.length; i<len; ++i) { 
    499                     this.writeNode(node, "Rule", style.rules[i]); 
     499                    this.writeNode("Rule", style.rules[i], node); 
    500500                } 
    501501                 
    502502                return node; 
     
    506506 
    507507                // add in optional name 
    508508                if(rule.name) { 
    509                     this.writeNode(node, "Name", rule.name); 
     509                    this.writeNode("Name", rule.name, node); 
    510510                } 
    511511                // add in optional title 
    512512                if(rule.title) { 
    513                     this.writeNode(node, "Title", rule.title); 
     513                    this.writeNode("Title", rule.title, node); 
    514514                } 
    515515                // add in optional description 
    516516                if(rule.description) { 
    517                     this.writeNode(node, "Abstract", rule.description); 
     517                    this.writeNode("Abstract", rule.description, node); 
    518518                } 
    519519                 
    520520                // add in LegendGraphic here 
    521521                 
    522522                // add in optional filters 
    523523                if(rule.elseFilter) { 
    524                     this.writeNode(node, "ElseFilter"); 
     524                    this.writeNode("ElseFilter", null, node); 
    525525                } else if(rule.filter) { 
    526                     this.writeNode(node, "ogc:Filter", rule.filter); 
     526                    this.writeNode("ogc:Filter", rule.filter, node); 
    527527                } 
    528528                 
    529529                // add in scale limits 
    530530                if(rule.minScaleDenominator != undefined) { 
    531531                    this.writeNode( 
    532                         node, "MinScaleDenominator", rule.minScaleDenominator 
     532                        "MinScaleDenominator", rule.minScaleDenominator, node 
    533533                    ); 
    534534                } 
    535535                if(rule.maxScaleDenominator != undefined) { 
    536536                    this.writeNode( 
    537                         node, "MaxScaleDenominator", rule.maxScaleDenominator 
     537                        "MaxScaleDenominator", rule.maxScaleDenominator, node 
    538538                    ); 
    539539                } 
    540540                 
     
    546546                    symbolizer = rule.symbolizer[type]; 
    547547                    if(symbolizer) { 
    548548                        this.writeNode( 
    549                             node, type + "Symbolizer", symbolizer 
     549                            type + "Symbolizer", symbolizer, node 
    550550                        ); 
    551551                    } 
    552552                } 
     
    568568            }, 
    569569            "LineSymbolizer": function(symbolizer) { 
    570570                var node = this.createElementNSPlus("LineSymbolizer"); 
    571                 this.writeNode(node, "Stroke", symbolizer); 
     571                this.writeNode("Stroke", symbolizer, node); 
    572572                return node; 
    573573            }, 
    574574            "Stroke": function(symbolizer) { 
     
    580580                // add in CssParameters 
    581581                if(symbolizer.strokeColor != undefined) { 
    582582                    this.writeNode( 
    583                         node, "CssParameter", 
    584                         {symbolizer: symbolizer, key: "strokeColor"} 
     583                        "CssParameter", 
     584                        {symbolizer: symbolizer, key: "strokeColor"}, 
     585                        node 
    585586                    ); 
    586587                } 
    587588                if(symbolizer.strokeOpacity != undefined) { 
    588589                    this.writeNode( 
    589                         node, "CssParameter", 
    590                         {symbolizer: symbolizer, key: "strokeOpacity"} 
     590                        "CssParameter", 
     591                        {symbolizer: symbolizer, key: "strokeOpacity"}, 
     592                        node 
    591593                    ); 
    592594                } 
    593595                if(symbolizer.strokeWidth != undefined) { 
    594596                    this.writeNode( 
    595                         node, "CssParameter", 
    596                         {symbolizer: symbolizer, key: "strokeWidth"} 
     597                        "CssParameter", 
     598                        {symbolizer: symbolizer, key: "strokeWidth"}, 
     599                        node 
    597600                    ); 
    598601                } 
    599602                return node; 
     
    609612                var node = this.createElementNSPlus("TextSymbolizer"); 
    610613                // add in optional Label 
    611614                if(symbolizer.label != null) { 
    612                     this.writeNode(node, "Label", symbolizer.label); 
     615                    this.writeNode("Label", symbolizer.label, node); 
    613616                } 
    614617                // add in optional Font 
    615618                if(symbolizer.fontFamily != null || 
    616619                   symbolizer.fontSize != null) { 
    617                     this.writeNode(node, "Font", symbolizer); 
     620                    this.writeNode("Font", symbolizer, node); 
    618621                } 
    619622                // add in optional Fill 
    620623                if(symbolizer.fillColor != null || 
    621624                   symbolizer.fillOpacity != null) { 
    622                     this.writeNode(node, "Fill", symbolizer); 
     625                    this.writeNode("Fill", symbolizer, node); 
    623626                } 
    624627                return node; 
    625628            }, 
     
    628631                // add in CssParameters 
    629632                if(symbolizer.fontFamily) { 
    630633                    this.writeNode( 
    631                         node, "CssParameter", 
    632                         {symbolizer: symbolizer, key: "fontFamily"} 
     634                        "CssParameter", 
     635                        {symbolizer: symbolizer, key: "fontFamily"}, 
     636                        node 
    633637                    ); 
    634638                } 
    635639                if(symbolizer.fontSize) { 
    636640                    this.writeNode( 
    637                         node, "CssParameter", 
    638                         {symbolizer: symbolizer, key: "fontSize"} 
     641                        "CssParameter", 
     642                        {symbolizer: symbolizer, key: "fontSize"}, 
     643                        node 
    639644                    ); 
    640645                } 
    641646                return node; 
     
    652657                    last = item.indexOf("}");  
    653658                    if(last > 0) { 
    654659                        this.writeNode( 
    655                             node, "ogc:PropertyName", 
    656                             {property: item.substring(0, last)} 
     660                            "ogc:PropertyName", 
     661                            {property: item.substring(0, last)}, 
     662                            node 
    657663                        ); 
    658664                        node.appendChild( 
    659665                            this.createTextNode(item.substring(++last)) 
     
    669675            }, 
    670676            "PolygonSymbolizer": function(symbolizer) { 
    671677                var node = this.createElementNSPlus("PolygonSymbolizer"); 
    672                 this.writeNode(node, "Fill", symbolizer); 
    673                 this.writeNode(node, "Stroke", symbolizer); 
     678                this.writeNode("Fill", symbolizer, node); 
     679                this.writeNode("Stroke", symbolizer, node); 
    674680                return node; 
    675681            }, 
    676682            "Fill": function(symbolizer) { 
     
    681687                // add in CssParameters 
    682688                if(symbolizer.fillColor) { 
    683689                    this.writeNode( 
    684                         node, "CssParameter", 
    685                         {symbolizer: symbolizer, key: "fillColor"} 
     690                        "CssParameter", 
     691                        {symbolizer: symbolizer, key: "fillColor"}, 
     692                        node 
    686693                    ); 
    687694                } 
    688695                if(symbolizer.fillOpacity) { 
    689696                    this.writeNode( 
    690                         node, "CssParameter", 
    691                         {symbolizer: symbolizer, key: "fillOpacity"} 
     697                        "CssParameter", 
     698                        {symbolizer: symbolizer, key: "fillOpacity"}, 
     699                        node 
    692700                    ); 
    693701                } 
    694702                return node; 
    695703            }, 
    696704            "PointSymbolizer": function(symbolizer) { 
    697705                var node = this.createElementNSPlus("PointSymbolizer"); 
    698                 this.writeNode(node, "Graphic", symbolizer); 
     706                this.writeNode("Graphic", symbolizer, node); 
    699707                return node; 
    700708            }, 
    701709            "Graphic": function(symbolizer) { 
    702710                var node = this.createElementNSPlus("Graphic"); 
    703711                if(symbolizer.externalGraphic != undefined) { 
    704                     this.writeNode(node, "ExternalGraphic", symbolizer); 
     712                    this.writeNode("ExternalGraphic", symbolizer, node); 
    705713                } else if(symbolizer.graphicName) { 
    706                     this.writeNode(node, "Mark", symbolizer); 
     714                    this.writeNode("Mark", symbolizer, node); 
    707715                } 
    708716                 
    709717                if(symbolizer.graphicOpacity != undefined) { 
    710                     this.writeNode(node, "Opacity", symbolizer.graphicOpacity); 
     718                    this.writeNode("Opacity", symbolizer.graphicOpacity, node); 
    711719                } 
    712720                if(symbolizer.pointRadius != undefined) { 
    713                     this.writeNode(node, "Size", symbolizer.pointRadius * 2); 
     721                    this.writeNode("Size", symbolizer.pointRadius * 2, node); 
    714722                } 
    715723                if(symbolizer.rotation != undefined) { 
    716                     this.writeNode(node, "Rotation", symbolizer.rotation); 
     724                    this.writeNode("Rotation", symbolizer.rotation, node); 
    717725                } 
    718726                return node; 
    719727            }, 
    720728            "ExternalGraphic": function(symbolizer) { 
    721729                var node = this.createElementNSPlus("ExternalGraphic"); 
    722730                this.writeNode( 
    723                     node, "OnlineResource", symbolizer.externalGraphic 
     731                    "OnlineResource", symbolizer.externalGraphic, node 
    724732                ); 
    725733                var format = symbolizer.graphicFormat || 
    726734                             this.getGraphicFormat(symbolizer.externalGraphic); 
    727                 this.writeNode(node, "Format", format); 
     735                this.writeNode("Format", format, node); 
    728736                return node; 
    729737            }, 
    730738            "Mark": function(symbolizer) { 
    731739                var node = this.createElementNSPlus("Mark"); 
    732                 this.writeNode(node, "WellKnownName", symbolizer.graphicName); 
    733                 this.writeNode(node, "Fill", symbolizer); 
    734                 this.writeNode(node, "Stroke", symbolizer); 
     740                this.writeNode("WellKnownName", symbolizer.graphicName, node); 
     741                this.writeNode("Fill", symbolizer, node); 
     742                this.writeNode("Stroke", symbolizer, node); 
    735743                return node; 
    736744            }, 
    737745            "WellKnownName": function(name) { 
     
    770778        } 
    771779    }, 
    772780     
    773     /** 
    774      * Methods below this point are of general use for versioned XML parsers. 
    775      * These are candidates for an abstract class. 
    776      */ 
    777      
    778     /** 
    779      * Method: getNamespacePrefix 
    780      * Get the namespace prefix for a given uri from the <namespaces> object. 
    781      * 
    782      * Returns: 
    783      * {String} A namespace prefix or null if none found. 
    784      */ 
    785     getNamespacePrefix: function(uri) { 
    786         var prefix = null; 
    787         if(uri == null) { 
    788             prefix = this.namespaces[this.defaultPrefix]; 
    789         } else { 
    790             var gotPrefix = false; 
    791             for(prefix in this.namespaces) { 
    792                 if(this.namespaces[prefix] == uri) { 
    793                     gotPrefix = true; 
    794                     break; 
    795                 } 
    796             } 
    797             if(!gotPrefix) { 
    798                 prefix = null; 
    799             } 
    800         } 
    801         return prefix; 
    802     }, 
    803  
    804  
    805     /** 
    806      * Method: readChildNodes 
    807      */ 
    808     readChildNodes: function(node, obj) { 
    809         var children = node.childNodes; 
    810         var child, group, reader, prefix, local; 
    811         for(var i=0, len=children.length; i<len; ++i) { 
    812             child = children[i]; 
    813             if(child.nodeType == 1) { 
    814                 prefix = this.getNamespacePrefix(child.namespaceURI); 
    815                 local = child.nodeName.split(":").pop(); 
    816                 group = this.readers[prefix]; 
    817                 if(group) { 
    818                     reader = group[local]; 
    819                     if(reader) { 
    820                         reader.apply(this, [child, obj]); 
    821                     } 
    822                 } 
    823             } 
    824         } 
    825     }, 
    826  
    827     /** 
    828      * Method: writeNode 
    829      * Shorthand for applying one of the named writers and appending the 
    830      *     results to a node.  If a qualified name is not provided for the 
    831      *     second argument (and a local name is used instead), the namespace 
    832      *     of the parent node will be assumed. 
    833      * 
    834      * Parameters: 
    835      * parent - {DOMElement} Result will be appended to this node. 
    836      * name - {String} The name of a node to generate.  If a qualified name 
    837      *     (e.g. "pre:Name") is used, the namespace prefix is assumed to be 
    838      *     in the <writers> group.  If a local name is used (e.g. "Name") then 
    839      *     the namespace of the parent is assumed. 
    840      * obj - {Object} Structure containing data for the writer. 
    841      * 
    842      * Returns: 
    843      * {DOMElement} The child node. 
    844      */ 
    845     writeNode: function(parent, name, obj) { 
    846         var prefix, local; 
    847