Project

General

Profile

Download (22.6 KB) Statistics
| Branch: | Tag: | Revision:
1
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2
 * full list of contributors). Published under the 2-clause BSD license.
3
 * See license.txt in the OpenLayers distribution or repository for the
4
 * full text of the license. */
5

    
6
/**
7
 * @requires OpenLayers/Format/XML.js
8
 * @requires OpenLayers/Format/GML/v3.js
9
 * @requires OpenLayers/Feature/Vector.js
10
 */
11

    
12
/**
13
 * Class: OpenLayers.Format.Atom
14
 * Read/write Atom feeds. Create a new instance with the
15
 *     <OpenLayers.Format.AtomFeed> constructor.
16
 *
17
 * Inherits from:
18
 *  - <OpenLayers.Format.XML>
19
 */
20
OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, {
21
    
22
    /**
23
     * Property: namespaces
24
     * {Object} Mapping of namespace aliases to namespace URIs.  Properties
25
     *     of this object should not be set individually.  Read-only.  All
26
     *     XML subclasses should have their own namespaces object.  Use
27
     *     <setNamespace> to add or set a namespace alias after construction.
28
     */
29
    namespaces: {
30
        atom: "http://www.w3.org/2005/Atom",
31
        georss: "http://www.georss.org/georss"
32
    },
33
    
34
    /**
35
     * APIProperty: feedTitle
36
     * {String} Atom feed elements require a title.  Default is "untitled".
37
     */
38
    feedTitle: "untitled",
39

    
40
    /**
41
     * APIProperty: defaultEntryTitle
42
     * {String} Atom entry elements require a title.  In cases where one is
43
     *     not provided in the feature attributes, this will be used.  Default
44
     *     is "untitled".
45
     */
46
    defaultEntryTitle: "untitled",
47

    
48
    /**
49
     * Property: gmlParse
50
     * {Object} GML Format object for parsing features
51
     * Non-API and only created if necessary
52
     */
53
    gmlParser: null,
54
    
55
    /**
56
     * APIProperty: xy
57
     * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)
58
     * For GeoRSS the default is (y,x), therefore: false
59
     */
60
    xy: false,
61
    
62
    /**
63
     * Constructor: OpenLayers.Format.AtomEntry
64
     * Create a new parser for Atom.
65
     *
66
     * Parameters:
67
     * options - {Object} An optional object whose properties will be set on
68
     *     this instance.
69
     */
70
    
71
    /**
72
     * APIMethod: read
73
     * Return a list of features from an Atom feed or entry document.
74
     
75
     * Parameters:
76
     * doc - {Element} or {String}
77
     *
78
     * Returns:
79
     * Array({<OpenLayers.Feature.Vector>})
80
     */
81
    read: function(doc) {
82
        if (typeof doc == "string") {
83
            doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
84
        }
85
        return this.parseFeatures(doc);
86
    },
87
    
88
    /**
89
     * APIMethod: write
90
     * Serialize or more feature nodes to Atom documents.
91
     *
92
     * Parameters:
93
     * features - {<OpenLayers.Feature.Vector>} or Array({<OpenLayers.Feature.Vector>})
94
     *
95
     * Returns:
96
     * {String} an Atom entry document if passed one feature node, or a feed
97
     * document if passed an array of feature nodes.
98
     */
99
    write: function(features) {
100
        var doc;
101
        if (OpenLayers.Util.isArray(features)) {
102
            doc = this.createElementNSPlus("atom:feed");
103
            doc.appendChild(
104
                this.createElementNSPlus("atom:title", {
105
                    value: this.feedTitle
106
                })
107
            );
108
            for (var i=0, ii=features.length; i<ii; i++) {
109
                doc.appendChild(this.buildEntryNode(features[i]));
110
            }
111
        }
112
        else {
113
            doc = this.buildEntryNode(features);
114
        }
115
        return OpenLayers.Format.XML.prototype.write.apply(this, [doc]);
116
    },
117
    
118
    /**
119
     * Method: buildContentNode
120
     *
121
     * Parameters:
122
     * content - {Object}
123
     *
124
     * Returns:
125
     * {DOMElement} an Atom content node.
126
     *
127
     * TODO: types other than text.
128
     */
129
    buildContentNode: function(content) {
130
        var node = this.createElementNSPlus("atom:content", {
131
            attributes: {
132
                type: content.type || null
133
            }
134
        });
135
        if (content.src) {
136
            node.setAttribute("src", content.src);
137
        } else {
138
            if (content.type == "text" || content.type == null) {
139
                node.appendChild(
140
                    this.createTextNode(content.value)
141
                );
142
            } else if (content.type == "html") {
143
                if (typeof content.value != "string") {
144
                    throw "HTML content must be in form of an escaped string";
145
                }
146
                node.appendChild(
147
                    this.createTextNode(content.value)
148
                );
149
            } else if (content.type == "xhtml") {
150
                node.appendChild(content.value);
151
            } else if (content.type == "xhtml" ||
152
                           content.type.match(/(\+|\/)xml$/)) {
153
                node.appendChild(content.value);
154
            }
155
            else { // MUST be a valid Base64 encoding
156
                node.appendChild(
157
                    this.createTextNode(content.value)
158
                );
159
            }
160
        }
161
        return node;
162
    },
163
    
164
    /**
165
     * Method: buildEntryNode
166
     * Build an Atom entry node from a feature object.
167
     *
168
     * Parameters:
169
     * feature - {<OpenLayers.Feature.Vector>}
170
     *
171
     * Returns:
172
     * {DOMElement} an Atom entry node.
173
     *
174
     * These entries are geared for publication using AtomPub.
175
     *
176
     * TODO: support extension elements
177
     */
178
    buildEntryNode: function(feature) {
179
        var attrib = feature.attributes;
180
        var atomAttrib = attrib.atom || {};
181
        var entryNode = this.createElementNSPlus("atom:entry");
182
        
183
        // atom:author
184
        if (atomAttrib.authors) {
185
            var authors = OpenLayers.Util.isArray(atomAttrib.authors) ?
186
                atomAttrib.authors : [atomAttrib.authors];
187
            for (var i=0, ii=authors.length; i<ii; i++) {
188
                entryNode.appendChild(
189
                    this.buildPersonConstructNode(
190
                        "author", authors[i]
191
                    )
192
                );
193
            }
194
        }
195
        
196
        // atom:category
197
        if (atomAttrib.categories) {
198
            var categories = OpenLayers.Util.isArray(atomAttrib.categories) ?
199
                atomAttrib.categories : [atomAttrib.categories];
200
            var category;
201
            for (var i=0, ii=categories.length; i<ii; i++) {
202
                category = categories[i];
203
                entryNode.appendChild(
204
                    this.createElementNSPlus("atom:category", {
205
                        attributes: {
206
                            term: category.term,
207
                            scheme: category.scheme || null,
208
                            label: category.label || null
209
                        }
210
                    })
211
                );
212
            }
213
        }
214
        
215
        // atom:content
216
        if (atomAttrib.content) {
217
            entryNode.appendChild(this.buildContentNode(atomAttrib.content));
218
        }
219
        
220
        // atom:contributor
221
        if (atomAttrib.contributors) {
222
            var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ?
223
                atomAttrib.contributors : [atomAttrib.contributors];
224
            for (var i=0, ii=contributors.length; i<ii; i++) {
225
                entryNode.appendChild(
226
                    this.buildPersonConstructNode(
227
                        "contributor",
228
                        contributors[i]
229
                        )
230
                    );
231
            }
232
        }
233
        
234
        // atom:id
235
        if (feature.fid) {
236
            entryNode.appendChild(
237
                this.createElementNSPlus("atom:id", {
238
                    value: feature.fid
239
                })
240
            );
241
        }
242
        
243
        // atom:link
244
        if (atomAttrib.links) {
245
            var links = OpenLayers.Util.isArray(atomAttrib.links) ?
246
                atomAttrib.links : [atomAttrib.links];
247
            var link;
248
            for (var i=0, ii=links.length; i<ii; i++) {
249
                link = links[i];
250
                entryNode.appendChild(
251
                    this.createElementNSPlus("atom:link", {
252
                        attributes: {
253
                            href: link.href,
254
                            rel: link.rel || null,
255
                            type: link.type || null,
256
                            hreflang: link.hreflang || null,
257
                            title: link.title || null,
258
                            length: link.length || null
259
                        }
260
                    })
261
                );
262
            }
263
        }
264
        
265
        // atom:published
266
        if (atomAttrib.published) {
267
            entryNode.appendChild(
268
                this.createElementNSPlus("atom:published", {
269
                    value: atomAttrib.published
270
                })
271
            );
272
        }
273
        
274
        // atom:rights
275
        if (atomAttrib.rights) {
276
            entryNode.appendChild(
277
                this.createElementNSPlus("atom:rights", {
278
                    value: atomAttrib.rights
279
                })
280
            );
281
        }
282
        
283
        // atom:source not implemented
284
        
285
        // atom:summary
286
        if (atomAttrib.summary || attrib.description) {
287
            entryNode.appendChild(
288
                this.createElementNSPlus("atom:summary", {
289
                    value: atomAttrib.summary || attrib.description
290
                })
291
            );
292
        }
293
        
294
        // atom:title
295
        entryNode.appendChild(
296
            this.createElementNSPlus("atom:title", {
297
                value: atomAttrib.title || attrib.title || this.defaultEntryTitle
298
            })
299
        );
300
        
301
        // atom:updated
302
        if (atomAttrib.updated) {
303
            entryNode.appendChild(
304
                this.createElementNSPlus("atom:updated", {
305
                    value: atomAttrib.updated
306
                })
307
            );
308
        }
309
        
310
        // georss:where
311
        if (feature.geometry) {
312
            var whereNode = this.createElementNSPlus("georss:where");
313
            whereNode.appendChild(
314
                this.buildGeometryNode(feature.geometry)
315
            );
316
            entryNode.appendChild(whereNode);
317
        }
318
        
319
        return entryNode;
320
    },
321
    
322
    /**
323
     * Method: initGmlParser
324
     * Creates a GML parser.
325
     */
326
    initGmlParser: function() {
327
        this.gmlParser = new OpenLayers.Format.GML.v3({
328
            xy: this.xy,
329
            featureNS: "http://example.com#feature",
330
            internalProjection: this.internalProjection,
331
            externalProjection: this.externalProjection
332
        });
333
    },
334
    
335
    /**
336
     * Method: buildGeometryNode
337
     * builds a GeoRSS node with a given geometry
338
     *
339
     * Parameters:
340
     * geometry - {<OpenLayers.Geometry>}
341
     *
342
     * Returns:
343
     * {DOMElement} A gml node.
344
     */
345
    buildGeometryNode: function(geometry) {
346
        if (!this.gmlParser) {
347
            this.initGmlParser();
348
        }
349
        var node = this.gmlParser.writeNode("feature:_geometry", geometry);
350
        return node.firstChild;
351
    },
352
    
353
    /**
354
     * Method: buildPersonConstructNode
355
     *
356
     * Parameters:
357
     * name - {String}
358
     * value - {Object}
359
     *
360
     * Returns:
361
     * {DOMElement} an Atom person construct node.
362
     *
363
     * Example:
364
     * >>> buildPersonConstructNode("author", {name: "John Smith"})
365
     * {<author><name>John Smith</name></author>}
366
     *
367
     * TODO: how to specify extension elements? Add to the oNames array?
368
     */
369
    buildPersonConstructNode: function(name, value) {
370
        var oNames = ["uri", "email"];
371
        var personNode = this.createElementNSPlus("atom:" + name);
372
        personNode.appendChild(
373
            this.createElementNSPlus("atom:name", {
374
                value: value.name
375
            })
376
        );
377
        for (var i=0, ii=oNames.length; i<ii; i++) {
378
            if (value[oNames[i]]) {
379
                personNode.appendChild(
380
                    this.createElementNSPlus("atom:" + oNames[i], {
381
                        value: value[oNames[i]]
382
                    })
383
                );
384
            }
385
        }
386
        return personNode;
387
    },
388
    
389
    /**
390
     * Method: getFirstChildValue
391
     *
392
     * Parameters:
393
     * node - {DOMElement}
394
     * nsuri - {String} Child node namespace uri ("*" for any).
395
     * name - {String} Child node name.
396
     * def - {String} Optional string default to return if no child found.
397
     *
398
     * Returns:
399
     * {String} The value of the first child with the given tag name.  Returns
400
     *     default value or empty string if none found.
401
     */
402
    getFirstChildValue: function(node, nsuri, name, def) {
403
        var value;
404
        var nodes = this.getElementsByTagNameNS(node, nsuri, name);
405
        if (nodes && nodes.length > 0) {
406
            value = this.getChildValue(nodes[0], def);
407
        } else {
408
            value = def;
409
        }
410
        return value;
411
    },
412
    
413
    /**
414
     * Method: parseFeature
415
     * Parse feature from an Atom entry node..
416
     *
417
     * Parameters:
418
     * node - {DOMElement} An Atom entry or feed node.
419
     *
420
     * Returns:
421
     * {<OpenLayers.Feature.Vector>}
422
     */
423
    parseFeature: function(node) {
424
        var atomAttrib = {};
425
        var value = null;
426
        var nodes = null;
427
        var attval = null;
428
        var atomns = this.namespaces.atom;
429
        
430
        // atomAuthor*
431
        this.parsePersonConstructs(node, "author", atomAttrib);
432
        
433
        // atomCategory*
434
        nodes = this.getElementsByTagNameNS(node, atomns, "category");
435
        if (nodes.length > 0) {
436
            atomAttrib.categories = [];
437
        }
438
        for (var i=0, ii=nodes.length; i<ii; i++) {
439
            value = {};
440
            value.term = nodes[i].getAttribute("term");
441
            attval = nodes[i].getAttribute("scheme");
442
            if (attval) { value.scheme = attval; }
443
            attval = nodes[i].getAttribute("label");
444
            if (attval) { value.label = attval; }
445
            atomAttrib.categories.push(value);
446
        }
447
        
448
        // atomContent?
449
        nodes = this.getElementsByTagNameNS(node, atomns, "content");
450
        if (nodes.length > 0) {
451
            value = {};
452
            attval = nodes[0].getAttribute("type");
453
            if (attval) {
454
                value.type = attval;
455
            }
456
            attval = nodes[0].getAttribute("src");
457
            if (attval) {
458
                value.src = attval;
459
            } else {
460
                if (value.type == "text" || 
461
                    value.type == "html" || 
462
                    value.type == null ) {
463
                    value.value = this.getFirstChildValue(
464
                                        node,
465
                                        atomns,
466
                                        "content",
467
                                        null
468
                                        );
469
                } else if (value.type == "xhtml" ||
470
                           value.type.match(/(\+|\/)xml$/)) {
471
                    value.value = this.getChildEl(nodes[0]);
472
                } else { // MUST be base64 encoded
473
                    value.value = this.getFirstChildValue(
474
                                        node,
475
                                        atomns,
476
                                        "content",
477
                                        null
478
                                        );
479
                }
480
                atomAttrib.content = value;
481
            }
482
        }
483
        
484
        // atomContributor*
485
        this.parsePersonConstructs(node, "contributor", atomAttrib);
486
        
487
        // atomId
488
        atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null);
489
        
490
        // atomLink*
491
        nodes = this.getElementsByTagNameNS(node, atomns, "link");
492
        if (nodes.length > 0) {
493
            atomAttrib.links = new Array(nodes.length);
494
        }
495
        var oAtts = ["rel", "type", "hreflang", "title", "length"];
496
        for (var i=0, ii=nodes.length; i<ii; i++) {
497
            value = {};
498
            value.href = nodes[i].getAttribute("href");
499
            for (var j=0, jj=oAtts.length; j<jj; j++) {
500
                attval = nodes[i].getAttribute(oAtts[j]);
501
                if (attval) {
502
                    value[oAtts[j]] = attval;
503
                }
504
            }
505
            atomAttrib.links[i] = value;
506
        }
507
        
508
        // atomPublished?
509
        value = this.getFirstChildValue(node, atomns, "published", null);
510
        if (value) {
511
            atomAttrib.published = value;
512
        }
513
        
514
        // atomRights?
515
        value = this.getFirstChildValue(node, atomns, "rights", null);
516
        if (value) {
517
            atomAttrib.rights = value;
518
        }
519
        
520
        // atomSource? -- not implemented
521
        
522
        // atomSummary?
523
        value = this.getFirstChildValue(node, atomns, "summary", null);
524
        if (value) {
525
            atomAttrib.summary = value;
526
        }
527
        
528
        // atomTitle
529
        atomAttrib.title = this.getFirstChildValue(
530
                                node, atomns, "title", null
531
                                );
532
        
533
        // atomUpdated
534
        atomAttrib.updated = this.getFirstChildValue(
535
                                node, atomns, "updated", null
536
                                );
537
        
538
        var featureAttrib = {
539
            title: atomAttrib.title,
540
            description: atomAttrib.summary,
541
            atom: atomAttrib
542
        };
543
        var geometry = this.parseLocations(node)[0];
544
        var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib);
545
        feature.fid = atomAttrib.id;
546
        return feature;
547
    },
548
    
549
    /**
550
     * Method: parseFeatures
551
     * Return features from an Atom entry or feed.
552
     *
553
     * Parameters:
554
     * node - {DOMElement} An Atom entry or feed node.
555
     *
556
     * Returns:
557
     * Array({<OpenLayers.Feature.Vector>})
558
     */
559
    parseFeatures: function(node) {
560
        var features = [];
561
        var entries = this.getElementsByTagNameNS(
562
            node, this.namespaces.atom, "entry"
563
        );
564
        if (entries.length == 0) {
565
            entries = [node];
566
        }
567
        for (var i=0, ii=entries.length; i<ii; i++) {
568
            features.push(this.parseFeature(entries[i]));
569
        }
570
        return features;
571
    },
572
    
573
    /**
574
     * Method: parseLocations
575
     * Parse the locations from an Atom entry or feed.
576
     *
577
     * Parameters:
578
     * node - {DOMElement} An Atom entry or feed node.
579
     *
580
     * Returns:
581
     * Array({<OpenLayers.Geometry>})
582
     */
583
    parseLocations: function(node) {
584
        var georssns = this.namespaces.georss;
585

    
586
        var locations = {components: []};
587
        var where = this.getElementsByTagNameNS(node, georssns, "where");
588
        if (where && where.length > 0) {
589
            if (!this.gmlParser) {
590
                this.initGmlParser();
591
            }
592
            for (var i=0, ii=where.length; i<ii; i++) {
593
                this.gmlParser.readChildNodes(where[i], locations);
594
            }
595
        }
596
        
597
        var components = locations.components;
598
        var point = this.getElementsByTagNameNS(node, georssns, "point");
599
        if (point && point.length > 0) {
600
            for (var i=0, ii=point.length; i<ii; i++) {
601
                var xy = OpenLayers.String.trim(
602
                            point[i].firstChild.nodeValue
603
                            ).split(/\s+/);
604
                if (xy.length !=2) {
605
                    xy = OpenLayers.String.trim(
606
                                point[i].firstChild.nodeValue
607
                                ).split(/\s*,\s*/);
608
                }
609
                components.push(new OpenLayers.Geometry.Point(xy[1], xy[0]));
610
            }
611
        }
612

    
613
        var line = this.getElementsByTagNameNS(node, georssns, "line");
614
        if (line && line.length > 0) {
615
            var coords;
616
            var p;
617
            var points;
618
            for (var i=0, ii=line.length; i<ii; i++) {
619
                coords = OpenLayers.String.trim(
620
                                line[i].firstChild.nodeValue
621
                                ).split(/\s+/);
622
                points = [];
623
                for (var j=0, jj=coords.length; j<jj; j+=2) {
624
                    p = new OpenLayers.Geometry.Point(coords[j+1], coords[j]);
625
                    points.push(p);
626
                }
627
                components.push(
628
                    new OpenLayers.Geometry.LineString(points)
629
                );
630
            }
631
        }        
632

    
633
        var polygon = this.getElementsByTagNameNS(node, georssns, "polygon");
634
        if (polygon && polygon.length > 0) {
635
            var coords;
636
            var p;
637
            var points;
638
            for (var i=0, ii=polygon.length; i<ii; i++) {
639
                coords = OpenLayers.String.trim(
640
                            polygon[i].firstChild.nodeValue
641
                            ).split(/\s+/);
642
                points = [];
643
                for (var j=0, jj=coords.length; j<jj; j+=2) {
644
                    p = new OpenLayers.Geometry.Point(coords[j+1], coords[j]);
645
                    points.push(p);
646
                }
647
                components.push(
648
                    new OpenLayers.Geometry.Polygon(
649
                        [new OpenLayers.Geometry.LinearRing(points)]
650
                    )
651
                );
652
            }
653
        }
654
        
655
        if (this.internalProjection && this.externalProjection) {
656
            for (var i=0, ii=components.length; i<ii; i++) {
657
                if (components[i]) {
658
                    components[i].transform(
659
                        this.externalProjection,
660
                        this.internalProjection
661
                    );
662
                }
663
            }
664
        }
665
        
666
        return components;
667
    },
668
    
669
    /**
670
     * Method: parsePersonConstruct
671
     * Parse Atom person constructs from an Atom entry node.
672
     *
673
     * Parameters:
674
     * node - {DOMElement} An Atom entry or feed node.
675
     * name - {String} Construcy name ("author" or "contributor")
676
     * data = {Object} Object in which to put parsed persons.
677
     *
678
     * Returns:
679
     * An {Object}.
680
     */
681
    parsePersonConstructs: function(node, name, data) {
682
        var persons = [];
683
        var atomns = this.namespaces.atom;
684
        var nodes = this.getElementsByTagNameNS(node, atomns, name);
685
        var oAtts = ["uri", "email"];
686
        for (var i=0, ii=nodes.length; i<ii; i++) {
687
            var value = {};
688
            value.name = this.getFirstChildValue(
689
                            nodes[i],
690
                            atomns,
691
                            "name",
692
                            null
693
                            );
694
            for (var j=0, jj=oAtts.length; j<jj; j++) {
695
                var attval = this.getFirstChildValue(
696
                            nodes[i],
697
                            atomns,
698
                            oAtts[j],
699
                            null);
700
                if (attval) {
701
                    value[oAtts[j]] = attval;
702
                }
703
            }
704
            persons.push(value);
705
        }
706
        if (persons.length > 0) {
707
            data[name + "s"] = persons;
708
        }
709
    },
710

    
711
    CLASS_NAME: "OpenLayers.Format.Atom"
712
});
(2-2/41)