OpenLayers OpenLayers

Changeset 7982

Show
Ignore:
Timestamp:
09/08/08 17:31:39 (3 months ago)
Author:
tschaub
Message:

Adding readers and writers properties to xml formats. These will contain public functions used in serializing and deserializing various formats. Shorthand methods for calling these readers & writers are writeNode and readNode. r=ahocever (closes #1722)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/openlayers/lib/OpenLayers/Format.js

    r7675 r7982  
    1313 */ 
    1414OpenLayers.Format = OpenLayers.Class({ 
     15     
     16    /** 
     17     * Property: options 
     18     * {Object} A reference to options passed to the constructor. 
     19     */ 
     20    options: null, 
    1521     
    1622    /** 
     
    5561    initialize: function(options) { 
    5662        OpenLayers.Util.extend(this, options); 
     63        this.options = options; 
     64    }, 
     65     
     66    /** 
     67     * APIMethod: destroy 
     68     * Clean up. 
     69     */ 
     70    destroy: function() { 
    5771    }, 
    5872 
  • trunk/openlayers/lib/OpenLayers/Format/XML.js

    r7870 r7982  
    2121     
    2222    /** 
     23     * Property: namespaces 
     24     * {Object} Mapping of namespace aliases to namespace URIs.  Properties 
     25     *     of this object should not be set individually. 
     26     */ 
     27    namespaces: {}, 
     28     
     29    /** 
     30     * Property: defaultNamespace 
     31     * {String} The default namespace alias for creating element nodes. 
     32     */ 
     33    defaultNamespace: null, 
     34     
     35    /** 
     36     * Property: readers 
     37     * Contains public functions, grouped by namespace prefix, that will 
     38     *     be applied when a namespaced node is found matching the function 
     39     *     name.  The function will be applied in the scope of this parser 
     40     *     with two arguments: the node being read and a context object passed 
     41     *     from the parent. 
     42     */ 
     43    readers: {}, 
     44     
     45    /** 
     46     * Property: writers 
     47     * As a compliment to the <readers> property, this structure contains public 
     48     *     writing functions grouped by namespace alias and named like the 
     49     *     node names they produce. 
     50     */ 
     51    writers: {}, 
     52 
     53    /** 
    2354     * Property: xmldom 
    2455     * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM 
     
    4576        } 
    4677        OpenLayers.Format.prototype.initialize.apply(this, [options]); 
     78    }, 
     79     
     80    /** 
     81     * APIMethod: destroy 
     82     * Clean up. 
     83     */ 
     84    destroy: function() { 
     85        this.xmldom = null; 
     86        OpenLayers.Format.prototype.destroy.apply(this, arguments); 
    4787    }, 
    4888 
     
    385425    }, 
    386426 
     427    /** 
     428     * Method: getNamespacePrefix 
     429     * Get the namespace prefix for a given uri from the <namespaces> object. 
     430     * 
     431     * Returns: 
     432     * {String} A namespace prefix or null if none found. 
     433     */ 
     434    getNamespacePrefix: function(uri) { 
     435        var prefix = null; 
     436        if(uri == null) { 
     437            prefix = this.defaultPrefix; 
     438        } else { 
     439            var prefix = null; 
     440            for(var p in this.namespaces) { 
     441                if(this.namespaces[p] == uri) { 
     442                    prefix = p; 
     443                    break; 
     444                } 
     445            } 
     446        } 
     447        return prefix; 
     448    }, 
     449 
     450    /** 
     451     * Method: createElementNSPlus 
     452     * Shorthand for creating namespaced elements with optional attributes and 
     453     *     child text nodes. 
     454     * 
     455     * Parameters: 
     456     * name - {String} The qualified node name. 
     457     * options - {Object} Optional object for node configuration. 
     458     * 
     459     * Valid options: 
     460     * uri - {String} Optional namespace uri for the element - supply a prefix 
     461     *     instead if the namespace uri is a property of the format's namespace 
     462     *     object. 
     463     * attributes - {Object} Optional attributes to be set using the 
     464     *     <setAttributes> method. 
     465     * value - {String} Optional text to be appended as a text node. 
     466     * 
     467     * Returns: 
     468     * {Element} An element node. 
     469     */ 
     470    createElementNSPlus: function(name, options) { 
     471        options = options || {}; 
     472        var loc = name.indexOf(":"); 
     473        // order of prefix preference 
     474        // 1. in the uri option 
     475        // 2. in the prefix option 
     476        // 3. in the qualified name 
     477        // 4. from the defaultPrefix 
     478        var uri = options.uri || this.namespaces[options.prefix]; 
     479        if(!uri) { 
     480            loc = name.indexOf(":"); 
     481            uri = this.namespaces[name.substring(0, loc)]; 
     482        } 
     483        if(!uri) { 
     484            uri = this.namespaces[this.defaultPrefix]; 
     485        } 
     486        var node = this.createElementNS(uri, name); 
     487        if(options.attributes) { 
     488            this.setAttributes(node, options.attributes); 
     489        } 
     490        if(options.value) { 
     491            node.appendChild(this.createTextNode(options.value)); 
     492        } 
     493        return node; 
     494    }, 
     495     
     496    /** 
     497     * Method: setAttributes 
     498     * Set multiple attributes given key value pairs from an object. 
     499     * 
     500     * Parameters: 
     501     * node - {Element} An element node. 
     502     * obj - {Object || Array} An object whose properties represent attribute 
     503     *     names and values represent attribute values.  If an attribute name 
     504     *     is a qualified name ("prefix:local"), the prefix will be looked up 
     505     *     in the parsers {namespaces} object.  If the prefix is found, 
     506     *     setAttributeNS will be used instead of setAttribute. 
     507     */ 
     508    setAttributes: function(node, obj) { 
     509        var value, loc, alias, uri; 
     510        for(var name in obj) { 
     511            if(obj[name] != null && obj[name].toString) { 
     512                value = obj[name].toString(); 
     513                // check for qualified attribute name ("prefix:local") 
     514                uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null; 
     515                this.setAttributeNS(node, uri, name, value); 
     516            } 
     517        } 
     518    }, 
     519 
     520    /** 
     521     * Method: readNode 
     522     * Shorthand for applying one of the named readers given the node 
     523     *     namespace and local name.  Readers take two args (node, obj) and 
     524     *     generally extend or modify the second. 
     525     * 
     526     * Parameters: 
     527     * node - {DOMElement} The node to be read (required). 
     528     * obj - {Object} The object to be modified (optional). 
     529     * 
     530     * Returns: 
     531     * {Object} The input object, modified (or a new one if none was provided). 
     532     */ 
     533    readNode: function(node, obj) { 
     534        if(!obj) { 
     535            obj = {}; 
     536        } 
     537        var prefix = this.getNamespacePrefix(node.namespaceURI); 
     538        var local = node.nodeName.split(":").pop(); 
     539        var group = this.readers[prefix]; 
     540        if(group) { 
     541            var reader = group[local] || group["*"]; 
     542            if(reader) { 
     543                reader.apply(this, [node, obj]); 
     544            } 
     545        } 
     546        return obj; 
     547    }, 
     548 
     549    /** 
     550     * Method: readChildNodes 
     551     * Shorthand for applying the named readers to all children of a node. 
     552     *     For each child of type 1 (element), <readSelf> is called. 
     553     * 
     554     * Parameters: 
     555     * node - {DOMElement} The node to be read (required). 
     556     * obj - {Object} The object to be modified (optional). 
     557     * 
     558     * Returns: 
     559     * {Object} The input object, modified. 
     560     */ 
     561    readChildNodes: function(node, obj) { 
     562        if(!obj) { 
     563            obj = {}; 
     564        } 
     565        var children = node.childNodes; 
     566        var child; 
     567        for(var i=0; i<children.length; ++i) { 
     568            child = children[i]; 
     569            if(child.nodeType == 1) { 
     570                this.readNode(child, obj); 
     571            } 
     572        } 
     573        return obj; 
     574    }, 
     575 
     576    /** 
     577     * Method: writeNode 
     578     * Shorthand for applying one of the named writers and appending the 
     579     *     results to a node.  If a qualified name is not provided for the 
     580     *     second argument (and a local name is used instead), the namespace 
     581     *     of the parent node will be assumed. 
     582     * 
     583     * Parameters: 
     584     * name - {String} The name of a node to generate.  If a qualified name 
     585     *     (e.g. "pre:Name") is used, the namespace prefix is assumed to be 
     586     *     in the <writers> group.  If a local name is used (e.g. "Name") then 
     587     *     the namespace of the parent is assumed.  If a local name is used 
     588     *     and no parent is supplied, then the default namespace is assumed. 
     589     * obj - {Object} Structure containing data for the writer. 
     590     * parent - {DOMElement} Result will be appended to this node.  If no parent 
     591     *     is supplied, the node will not be appended to anything. 
     592     * 
     593     * Returns: 
     594     * {DOMElement} The child node. 
     595     */ 
     596    writeNode: function(name, obj, parent) { 
     597        var prefix, local; 
     598        var split = name.indexOf(":"); 
     599        if(split > 0) { 
     600            prefix = name.substring(0, split); 
     601            local = name.substring(split + 1); 
     602        } else { 
     603            if(parent) { 
     604                prefix = this.getNamespacePrefix(parent.namespaceURI); 
     605            } else { 
     606                prefix = this.defaultPrefix; 
     607            } 
     608            local = name; 
     609        } 
     610        var child = this.writers[prefix][local].apply(this, [obj]); 
     611        if(parent) { 
     612            parent.appendChild(child); 
     613        } 
     614        return child; 
     615    }, 
     616 
    387617    CLASS_NAME: "OpenLayers.Format.XML"  
    388618 
  • trunk/openlayers/tests/Format.html

    r6724 r7982  
    55 
    66    function test_Format_constructor(t) { 
    7         t.plan(4); 
     7        t.plan(5); 
    88     
    99        var options = {'foo': 'bar'}; 
     
    1414        t.eq(typeof format.read, "function", "format has a read function"); 
    1515        t.eq(typeof format.write, "function", "format has a write function"); 
     16        t.eq(format.options, options, "format.options correctly set"); 
    1617    } 
    1718 
  • trunk/openlayers/tests/Format/XML.html

    r6719 r7982  
    2828 
    2929    function test_Format_XML_constructor(t) {  
    30         t.plan(5);  
     30        t.plan(13);  
    3131          
    3232        var options = {'foo': 'bar'};  
     
    3939 
    4040        t.ok(!window.ActiveXObject || format.xmldom, "browsers with activeX must have xmldom"); 
     41         
     42        // test namespaces 
     43        t.ok(format.namespaces instanceof Object, "format has namespace object"); 
     44        var namespaces = {"foo": "bar"}; 
     45        format = new OpenLayers.Format.XML({namespaces: namespaces}); 
     46        t.eq(format.namespaces, namespaces, "format.namespaces correctly set in constructor"); 
     47         
     48        // test default prefix 
     49        t.eq(format.defaultPrefix, null, "defaultPrefix is null by default"); 
     50        format = new OpenLayers.Format.XML({defaultPrefix: "foo"}); 
     51        t.eq(format.defaultPrefix, "foo", "defaultPrefix correctly set in constructor"); 
     52 
     53        // test readers 
     54        t.ok(format.readers instanceof Object, "format has readers object"); 
     55        var readers = {"foo": "bar"}; 
     56        format = new OpenLayers.Format.XML({readers: readers}); 
     57        t.eq(format.readers, readers, "format.readers correctly set in constructor"); 
     58 
     59        // test readers 
     60        t.ok(format.writers instanceof Object, "format has writers object"); 
     61        var writers = {"foo": "bar"}; 
     62        format = new OpenLayers.Format.XML({writers: writers}); 
     63        t.eq(format.writers, writers, "format.writers correctly set in constructor"); 
     64    } 
     65     
     66    function test_destroy(t) { 
     67        t.plan(1); 
     68        var format = new OpenLayers.Format.XML(); 
     69        format.destroy(); 
     70        t.eq(format.xmldom, null, "xmldom set to null for all browsers"); 
    4171    } 
    4272 
     
    261291        t.ok(found === false, "returns false for bad attribute"); 
    262292    } 
     293     
     294    function test_getNamespacePrefix(t) { 
     295        t.plan(6); 
     296         
     297        // test that getNamespacePrefix returns null with no ns defined 
     298        var format = new OpenLayers.Format.XML();         
     299        var got = format.getNamespacePrefix("http://example.com/foo"); 
     300        t.eq(got, null, "returns null when no namespaces are defined"); 
     301         
     302        format.defaultPrefix = "def"; 
     303        format.namespaces = { 
     304            "def": "http://example.com/default", 
     305            "foo": "http://example.com/foo", 
     306            "bar": "http://example.com/bar" 
     307        }; 
     308         
     309        var cases = [ 
     310            {uri: null, expect: "def"}, 
     311            {uri: "http://example.com/default", expect: "def"}, 
     312            {uri: "http://example.com/foo", expect: "foo"}, 
     313            {uri: "http://example.com/bar", expect: "bar"},             
     314            {uri: "http://example.com/nothing", expect: null} 
     315        ]; 
     316         
     317        var test; 
     318        for(var i=0; i<cases.length; ++i) { 
     319            test = cases[i]; 
     320            t.eq(format.getNamespacePrefix(test.uri), test.expect, 
     321                 "uri: " + test.uri + " works"); 
     322        } 
     323 
     324    } 
     325     
     326    function test_readChildNodes(t) { 
     327         
     328        var text = "<?xml version='1.0' encoding='UTF-8'?>" + 
     329        "<container xmlns='http://example.com/foo'>" + 
     330            "<marker name='my marker 1'>" + 
     331                "<position>" + 
     332                    "<lon>-180</lon>" + 
     333                    "<lat>90</lat>" + 
     334                "</position>" + 
     335                "<detail>some text for first marker</detail>" + 
     336                "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/1'/>" + 
     337            "</marker>" + 
     338            "<marker name='my marker 2'>" + 
     339                "<position>" + 
     340                    "<lon>180</lon>" + 
     341                    "<lat>-90</lat>" + 
     342                "</position>" + 
     343                "<detail>some text for second marker</detail>" + 
     344                "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/2'/>" + 
     345            "</marker>" + 
     346        "</container>"; 
     347         
     348        var expect = [ 
     349            new OpenLayers.Feature.Vector( 
     350                new OpenLayers.Geometry.Point(-180, 90), 
     351                { 
     352                    name: 'my marker 1', 
     353                    link: 'http://host/path/1', 
     354                    detail: 'some text for first marker' 
     355                } 
     356            ), 
     357            new OpenLayers.Feature.Vector( 
     358                new OpenLayers.Geometry.Point(180, -90), 
     359                { 
     360                    name: 'my marker 2', 
     361                    link: 'http://host/path/2', 
     362                    detail: 'some text for second marker' 
     363                } 
     364            ) 
     365        ]; 
     366         
     367        var format = new OpenLayers.Format.XML({ 
     368            defaultPrefix: "foo", 
     369            namespaces: { 
     370                "foo": "http://example.com/foo", 
     371                "atom": "http://www.w3.org/2005/Atom" 
     372            }, 
     373            readers: { 
     374                "foo": { 
     375                    "container": function(node, obj) { 
     376                        var list = []; 
     377                        this.readChildNodes(node, list); 
     378                        obj.list = list; 
     379                    }, 
     380                    "marker": function(node, list) { 
     381                        var feature = new OpenLayers.Feature.Vector(); 
     382                        feature.attributes.name = node.getAttribute("name"); 
     383                        this.readChildNodes(node, feature); 
     384                        list.push(feature); 
     385                    }, 
     386                    "position": function(node, feature) { 
     387                        var obj = {}; 
     388                        this.readChildNodes(node, obj); 
     389                        feature.geometry = new OpenLayers.Geometry.Point(obj.x, obj.y); 
     390                    }, 
     391                    "lon": function(node, obj) { 
     392                        obj.x = this.getChildValue(node); 
     393                    }, 
     394                    "lat": function(node, obj) { 
     395                        obj.y = this.getChildValue(node); 
     396                    }, 
     397                    "detail": function(node, feature) { 
     398                        feature.attributes.detail = this.getChildValue(node); 
     399                    } 
     400                }, 
     401                "atom": { 
     402                    "link": function(node, feature) { 
     403                        feature.attributes.link = node.getAttribute("href"); 
     404                    }                     
     405                } 
     406            } 
     407        }); 
     408         
     409        // convert text to document node 
     410        var doc = format.read(text); 
     411        // read child nodes to get back some object 
     412        var obj = format.readChildNodes(doc); 
     413        // start comparing what we got to what we expect 
     414        var got = obj.list; 
     415         
     416        t.plan(11); 
     417        t.eq(got.length, expect.length, "correct number of items parsed"); 
     418        t.eq(got[0].geometry.x, expect[0].geometry.x, "correct x coord parsed for marker 1"); 
     419        t.eq(got[0].geometry.y, expect[0].geometry.y, "correct y coord parsed for marker 1"); 
     420        t.eq(got[0].attributes.name, expect[0].attributes.name, "correct name parsed for marker 1"); 
     421        t.eq(got[0].attributes.detail, expect[0].attributes.detail, "correct detail parsed for marker 1"); 
     422        t.eq(got[0].attributes.link, expect[0].attributes.link, "correct link parsed for marker 1"); 
     423        t.eq(got[1].geometry.x, expect[1].geometry.x, "correct x coord parsed for marker 2"); 
     424        t.eq(got[1].geometry.y, expect[1].geometry.y, "correct y coord parsed for marker 2"); 
     425        t.eq(got[1].attributes.name, expect[1].attributes.name, "correct name parsed for marker 2"); 
     426        t.eq(got[1].attributes.detail, expect[1].attributes.detail, "correct detail parsed for marker 2"); 
     427        t.eq(got[1].attributes.link, expect[1].attributes.link, "correct link parsed for marker 2"); 
     428         
     429    } 
     430     
     431    function test_writeNode(t) { 
     432         
     433        var features = [ 
     434            new OpenLayers.Feature.Vector( 
     435                new OpenLayers.Geometry.Point(-180, 90), 
     436                { 
     437                    name: 'my marker 1', 
     438                    link: 'http://host/path/1', 
     439                    detail: 'some text for first marker' 
     440                } 
     441            ), 
     442            new OpenLayers.Feature.Vector( 
     443                new OpenLayers.Geometry.Point(180, -90), 
     444                { 
     445                    name: 'my marker 2', 
     446                    link: 'http://host/path/2', 
     447                    detail: 'some text for second marker' 
     448                } 
     449            ) 
     450        ]; 
     451 
     452        var expect = "<?xml version='1.0' encoding='UTF-8'?>" + 
     453        "<container xmlns='http://example.com/foo'>" + 
     454            "<marker name='my marker 1'>" + 
     455                "<position>" + 
     456                    "<lon>-180</lon>" + 
     457                    "<lat>90</lat>" + 
     458                "</position>" + 
     459                "<detail>some text for first marker</detail>" + 
     460                "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/1'/>" + 
     461            "</marker>" + 
     462            "<marker name='my marker 2'>" + 
     463                "<position>" + 
     464                    "<lon>180</lon>" + 
     465                    "<lat>-90</lat>" + 
     466                "</position>" + 
     467                "<detail>some text for second marker</detail>" + 
     468                "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/2'/>" + 
     469            "</marker>" + 
     470        "</container>"; 
     471         
     472        var format = new OpenLayers.Format.XML({ 
     473            defaultPrefix: "foo", 
     474            namespaces: { 
     475                "foo": "http://example.com/foo", 
     476                "atom": "http://www.w3.org/2005/Atom" 
     477            }, 
     478            writers: { 
     479                "foo": { 
     480                    "container": function(features) { 
     481                        var node = this.createElementNSPlus("container"); 
     482                        var feature; 
     483                        for(var i=0; i<features.length; ++i) { 
     484                            feature = features[i]; 
     485                            this.writeNode("marker", features[i], node); 
     486                        } 
     487                        return node; 
     488                    }, 
     489                    "marker": function(feature) { 
     490                        var node = this.createElementNSPlus("marker", { 
     491                            attributes: {name: feature.attributes.name} 
     492                        }); 
     493                        this.writeNode("position", feature.geometry, node); 
     494                        this.writeNode("detail", feature.attributes.detail, node); 
     495                        this.writeNode("atom:link", feature.attributes.link, node); 
     496                        return node; 
     497                    }, 
     498                    "position": function(geometry) { 
     499                        var node = this.createElementNSPlus("position"); 
     500                        this.writeNode("lon", geometry.x, node); 
     501                        this.writeNode("lat", geometry.y, node); 
     502                        return node; 
     503                    }, 
     504                    "lon": function(x) { 
     505                        return this.createElementNSPlus("lon", { 
     506                            value: x 
     507                        }); 
     508                    }, 
     509                    "lat": function(y) { 
     510                        return this.createElementNSPlus("lat", { 
     511                            value: y 
     512                        }); 
     513                    }, 
     514                    "detail": function(text) { 
     515                        return this.createElementNSPlus("detail", { 
     516                            value: text 
     517                        }); 
     518                    } 
     519                }, 
     520                "atom": { 
     521                    "link": function(href) { 
     522                        return this.createElementNSPlus("atom:link", { 
     523                            attributes: {href: href} 
     524                        }); 
     525                    } 
     526                } 
     527            } 
     528             
     529        }); 
     530         
     531        t.plan(1); 
     532        // test that we get what we expect from writeNode 
     533        var got = format.writeNode("container", features); 
     534        t.xml_eq(got, expect, "features correctly written"); 
     535    } 
     536     
     537    function test_createElementNSPlus(t) { 
     538         
     539        var format = new OpenLayers.Format.XML({ 
     540            defaultPrefix: "def", 
     541            namespaces: { 
     542                "def": "http://example.com/default", 
     543                "foo": "http://example.com/foo", 
     544                "bar": "http://example.com/bar" 
     545            } 
     546        }); 
     547         
     548        var cases = [ 
     549            { 
     550                description: "unprefixed name with default options", 
     551                node: format.createElementNSPlus("FooNode"), 
     552                expect: "<def:FooNode xmlns:def='http://example.com/default'/>" 
     553            }, { 
     554                description: "def prefixed name with default options", 
     555                node: format.createElementNSPlus("def:FooNode"), 
     556                expect: "<def:FooNode xmlns:def='http://example.com/default'/>" 
     557            }, { 
     558                description: "foo prefixed name with default options", 
     559                node: format.createElementNSPlus("foo:FooNode"), 
     560                expect: "<foo:FooNode xmlns:foo='http://example.com/foo'/>" 
     561            }, { 
     562                description: "unprefixed name with uri option", 
     563                node: format.createElementNSPlus("FooNode", { 
     564                    uri: "http://example.com/elsewhere" 
     565                }), 
     566                expect: "<FooNode xmlns='http://example.com/elsewhere'/>" 
     567            }, { 
     568                description: "foo prefixed name with uri option (overriding format.namespaces)", 
     569                node: format.createElementNSPlus("foo:FooNode", { 
     570                    uri: "http://example.com/elsewhere" 
     571                }), 
     572                expect: "<foo:FooNode xmlns:foo='http://example.com/elsewhere'/>" 
     573            }, { 
     574                description: "foo prefixed name with attributes option", 
     575                node: format.createElementNSPlus("foo:FooNode", { 
     576                    attributes: { 
     577                        "id": "123", 
     578                        "foo:attr1": "namespaced attribute 1", 
     579                        "bar:attr2": "namespaced attribute 2" 
     580                    } 
     581                }), 
     582                expect: "<foo:FooNode xmlns:foo='http://example.com/foo' xmlns:bar='http://example.com/bar' id='123' foo:attr1='namespaced attribute 1' bar:attr2='namespaced attribute 2'/>" 
     583            }, { 
     584                description: "foo prefixed name with attributes and value options", 
     585                node: format.createElementNSPlus("foo:FooNode", { 
     586                    attributes: {"id": "123"}, 
     587                    value: "text value" 
     588                }), 
     589                expect: "<foo:FooNode xmlns:foo='http://example.com/foo' id='123'>text value<" + "/foo:FooNode>" 
     590            } 
     591        ]; 
     592         
     593        t.plan(cases.length); 
     594        var test; 
     595        for(var i=0; i<cases.length; ++i) { 
     596            test = cases[i]; 
     597            t.xml_eq(test.node, test.expect, test.description); 
     598        } 
     599         
     600    } 
     601     
     602    function test_setAttributes(t) { 
     603         
     604        var format = new OpenLayers.Format.XML({ 
     605            defaultPrefix: "def", 
     606            namespaces: { 
     607                "def": "http://example.com/default", 
     608                "foo": "http://example.com/foo", 
     609                "bar": "http://example.com/bar" 
     610            } 
     611        }); 
     612         
     613        var cases = [ 
     614            { 
     615                description: "unprefixed attribute", 
     616                node: format.createElementNSPlus("foo:Node"), 
     617                attributes: {"id": "123"}, 
     618                expect: "<foo:Node xmlns:foo='http://example.com/foo' id='123'/>" 
     619            }, { 
     620                description: "foo prefixed attribute", 
     621                node: format.createElementNSPlus("foo:Node"), 
     622                attributes: {"foo:id": "123"}, 
     623                expect: "<foo:Node xmlns:foo='http://example.com/foo' foo:id='123'/>" 
     624            }, { 
     625                description: "foo prefixed attribute with def prefixed node", 
     626                node: format.createElementNSPlus("def:Node"), 
     627                attributes: {"foo:id": "123"}, 
     628                expect: "<def:Node xmlns:def='http://example.com/default' xmlns:foo='http://example.com/foo' foo:id='123'/>" 
     629            }, { 
     630                description: "multiple attributes", 
     631                node: format.createElementNSPlus("def:Node"), 
     632                attributes: {"id": "123", "foo": "bar"}, 
     633                expect: "<def:Node xmlns:def='http://example.com/default' id='123' foo='bar'/>" 
     634            } 
     635        ]; 
     636         
     637        t.plan(cases.length); 
     638        var test; 
     639        for(var i=0; i<cases.length; ++i) { 
     640            test = cases[i]; 
     641            format.setAttributes(test.node, test.attributes); 
     642            t.xml_eq(test.node, test.expect, test.description); 
     643        } 
     644         
     645    } 
    263646 
    264647    </script>