initial mockup
[microdatajs:microdatajs.git] / microdata.js
1 function splitTypes(s) {
2     if (s) return s.split(/\s+/);
3     return [];
4 }
5
6 function inList(item, list) {
7     for (var i = 0; i < list.length; i++) {
8         if (item == list[i])
9             return true;
10     }
11     return false;
12 }
13
14 function isSubset(sub, sup) {
15     for (var i = 0; i < sub.length; i++) {
16         if (!inList(sub[i], sup))
17             return false;
18     }
19     return true;
20 }
21
22 function getElementsByFilter(pred) {
23     var elems = [];
24     function filterNode(elem) {
25         if (pred(elem))
26             elems.push(elem);
27         for (var child = elem.firstChild; child; child = child.nextSibling) {
28             if (child.nodeType == 1) {
29                 filterNode(child);
30             }
31         }
32     }
33     filterNode(document.documentElement);
34     return elems;
35 }
36
37 function map(list, func) {
38     var ret = [];
39     for (var i = 0; i < list.length; i++) {
40         ret.push(func(list[i]));
41     }
42     return ret;
43 }
44
45 function filter(list, pred) {
46     var ret = [];
47     for (var i = 0; i < list.length; i++) {
48         if (pred(list[i]))
49             ret.push(list[i]);
50     }
51     return ret;
52 }
53
54 function filterItems(correspondingItem) {
55     return filter(itemNodes, function (x) {
56                       return getCorrespondingItem(x) == correspondingItem;
57                   });
58 }
59
60 Element.prototype.__defineGetter__('item', function () { return this.getAttribute('item'); });
61
62 Element.prototype.__defineGetter__('itemprop', function () { return this.getAttribute('itemprop'); });
63
64 Element.prototype.__defineGetter__("properties",
65 function() {
66     // If the element has an item attribute, returns an
67     // HTMLPropertyCollection object with all the element's
68     // properties.
69     var elems;
70     if (this.hasAttribute('item')) {
71         var itemelem = this; /* this in inline function is window */
72         elems = getElementsByFilter(function(e){return e.hasAttribute('itemprop') &&
73                                                 splitTypes(e.getAttribute('itemprop')).length &&
74                                                 getCorrespondingItem(e) == itemelem;});
75     } else {
76         // Otherwise, an empty HTMLPropertyCollection object.
77         elems = [];
78     }
79
80     elems.names = [];
81     for (var i = 0; i < elems.length; i++) {
82         var elemNames = splitTypes(elems[i].getAttribute('itemprop'));
83         for (var j = 0; j < elemNames.length; j++) {
84             if (!inList(elemNames[j], elems.names))
85                 elems.names.push(elemNames[j]);
86         }
87     }
88
89     elems.item = function(idx){try{return this[idx];}catch(x){return null;}};
90
91     elems.namedItem = function (name) {
92         var namedElems = filter(elems, function(e){return inList(name, splitTypes(e.getAttribute('itemprop')));});
93         namedElems.item = function(idx){return this[idx];};
94         namedElems.contents = map(namedElems, function(e){return e.content;});
95         return namedElems;
96     };
97
98     return elems;
99 });
100
101 Element.prototype.__defineGetter__("content",
102 function() {
103     if (this.tagName == 'META')
104         return this.getAttribute('content');
105     if (this.tagName == 'AUDIO' || this.tagName == 'EMBED' ||
106         this.tagName == 'IFRAME' || this.tagName == 'IMG' ||
107         this.tagName == 'SOURCE' || this.tagName == 'VIDEO')
108         return this.src;
109     if (this.tagName == 'A' || this.tagName == 'AREA' || this.tagName == 'LINK')
110         return this.href;
111     if (this.tagName == 'OBJECT')
112         return this.data;
113     if (this.tagName == 'TIME' && this.hasAttribute('datetime'))
114         return this.getAttribute('datetime');
115     return this.textContent;
116 });
117
118 Element.prototype.__defineGetter__('subject', function () { return this.getAttribute('subject'); });
119
120 function getCorrespondingItem(node) {
121     if (node.hasAttribute('subject'))
122         return document.getElementById(node.getAttribute('subject'));
123     var ancestor = node.parentNode;
124     while (ancestor && ancestor.nodeType == 1) {
125         if (ancestor.hasAttribute('item'))
126             return ancestor;
127         ancestor = ancestor.parentNode;
128     }
129     return null;
130 }
131
132 Document.prototype.getItems = function(typeNames) {
133     //var xpr = document.evaluate('//*[@item]', root, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
134     //this.length = xpr.snapshotLength;
135     //this.item = function(i) { return xpr.snapshotItem(i); };
136
137     var types = splitTypes(typeNames);
138     var items = getElementsByFilter(function(e){return e.hasAttribute('item') &&
139                                                 isSubset(types, splitTypes(e.getAttribute('item'))) &&
140                                                 getCorrespondingItem(e) == null;});
141
142     items.item = function(idx){return items[idx];};
143
144     return items;
145 };
146
147 // FIXME: not in spec
148 Document.prototype.__defineGetter__('items', function () { return this.getItems(); });