!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 var EXPORTED_SYMBOLS = ["Microformats", "adr", "tag", "hCard", "hCalendar", "geo"];
2
3 var Microformats = {
4 /* When a microformat is added, the name is placed in this list */
5 list: [],
6 /* Custom iterator so that microformats can be enumerated as */
7 /* for (i in Microformats) */
__iterator__
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8 __iterator__: function () {
9 for (let i=0; i < this.list.length; i++) {
10 yield this.list[i];
11 }
12 },
13 /**
14 * Retrieves microformats objects of the given type from a document
15 *
16 * @param name The name of the microformat (required)
17 * @param rootElement The DOM element at which to start searching (required)
18 * @param options Literal object with the following options:
19 * recurseExternalFrames - Whether or not to search child frames
20 * that reference external pages (with a src attribute)
21 * for microformats (optional - defaults to true)
22 * showHidden - Whether or not to add hidden microformat
23 * (optional - defaults to false)
24 * debug - Whether or not we are in debug mode (optional
25 * - defaults to false)
26 * @param targetArray An array of microformat objects to which is added the results (optional)
27 * @return A new array of microformat objects or the passed in microformat
28 * object array with the new objects added
29 */
get
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
30 get: function(name, rootElement, options, targetArray) {
isAncestor
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
31 function isAncestor(haystack, needle) {
32 var parent = needle;
33 while (parent = parent.parentNode) {
34 /* We need to check parentNode because defaultView.frames[i].frameElement */
35 /* isn't a real DOM node */
36 if (parent == needle.parentNode) {
37 return true;
38 }
39 }
40 return false;
41 }
42 if (!Microformats[name] || !rootElement) {
43 return;
44 }
45 targetArray = targetArray || [];
46
47 /* Root element might not be the document - we need the document's default view */
48 /* to get frames and to check their ancestry */
49 var defaultView = rootElement.defaultView || rootElement.ownerDocument.defaultView;
50 var rootDocument = rootElement.ownerDocument || rootElement;
51
52 /* If recurseExternalFrames is undefined or true, look through all child frames for microformats */
53 if (!options || !options.hasOwnProperty("recurseExternalFrames") || options.recurseExternalFrames) {
54 if (defaultView && defaultView.frames.length > 0) {
55 for (let i=0; i < defaultView.frames.length; i++) {
56 if (isAncestor(rootDocument, defaultView.frames[i].frameElement)) {
57 Microformats.get(name, defaultView.frames[i].document, options, targetArray);
58 }
59 }
60 }
61 }
62
63 /* Get the microformat nodes for the document */
64 var microformatNodes = [];
65 if (Microformats[name].className) {
66 microformatNodes = Microformats.getElementsByClassName(rootElement,
67 Microformats[name].className);
68 /* alternateClassName is for cases where a parent microformat is inferred by the children */
69 /* If we find alternateClassName, the entire document becomes the microformat */
70 if ((microformatNodes.length == 0) && Microformats[name].alternateClassName) {
71 var altClass = Microformats.getElementsByClassName(rootElement, Microformats[name].alternateClassName);
72 if (altClass.length > 0) {
73 microformatNodes.push(rootElement);
74 }
75 }
76 } else if (Microformats[name].attributeValues) {
77 microformatNodes =
78 Microformats.getElementsByAttribute(rootElement,
79 Microformats[name].attributeName,
80 Microformats[name].attributeValues);
81
82 }
83 /* Create objects for the microformat nodes and put them into the microformats */
84 /* array */
85 for (let i = 0; i < microformatNodes.length; i++) {
86 /* If showHidden undefined or false, don't add microformats to the list that aren't visible */
87 if (!options || !options.hasOwnProperty("showHidden") || !options.showHidden) {
88 if (microformatNodes[i].ownerDocument) {
89 if (microformatNodes[i].getBoundingClientRect) {
90 var box = microformatNodes[i].getBoundingClientRect();
91 box.width = box.right - box.left;
92 box.height = box.bottom - box.top;
93 } else {
94 var box = microformatNodes[i].ownerDocument.getBoxObjectFor(microformatNodes[i]);
95 }
96 if ((box.height == 0) || (box.width == 0)) {
97 continue;
98 }
99 }
100 }
101 try {
102 if (options && options.debug) {
103 /* Don't validate in the debug case so that we don't get errors thrown */
104 /* in the debug case, we want all microformats, even if they are invalid */
105 targetArray.push(new Microformats[name].mfObject(microformatNodes[i], false));
106 } else {
107 targetArray.push(new Microformats[name].mfObject(microformatNodes[i], true));
108 }
109 } catch (ex) {
110 /* Creation of individual object probably failed because it is invalid. */
111 /* This isn't a problem, because the page might have invalid microformats */
112 }
113 }
114 return targetArray;
115 },
116 /**
117 * Counts microformats objects of the given type from a document
118 *
119 * @param name The name of the microformat (required)
120 * @param rootElement The DOM element at which to start searching (required)
121 * @param options Literal object with the following options:
122 * recurseExternalFrames - Whether or not to search child frames
123 * that reference external pages (with a src attribute)
124 * for microformats (optional - defaults to true)
125 * showHidden - Whether or not to add hidden microformat
126 * (optional - defaults to false)
127 * debug - Whether or not we are in debug mode (optional
128 * - defaults to false)
129 * @return The new count
130 */
count
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
131 count: function(name, rootElement, options) {
132 var mfArray = Microformats.get(name, rootElement, options);
133 if (mfArray) {
134 return mfArray.length;
135 }
136 return 0;
137 },
138 /**
139 * Returns true if the passed in node is a microformat. Does NOT return true
140 * if the passed in node is a child of a microformat.
141 *
142 * @param node DOM node to check
143 * @return true if the node is a microformat, false if it is not
144 */
isMicroformat
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
145 isMicroformat: function(node) {
146 for (let i in Microformats)
147 {
148 if (Microformats[i].className) {
149 if (Microformats.matchClass(node, Microformats[i].className)) {
150 return true;
151 }
152 } else {
153 var attribute;
154 if (attribute = node.getAttribute(Microformats[i].attributeName)) {
155 var attributeList = Microformats[i].attributeValues.split(" ");
156 for (let j=0; j < attributeList.length; j++) {
157 if (attribute.match("(^|\\s)" + attributeList[j] + "(\\s|$)")) {
158 return true;
159 }
160 }
161 }
162 }
163 }
164 return false;
165 },
166 /**
167 * This function searches a given nodes ancestors looking for a microformat
168 * and if it finds it, returns it. It does NOT include self, so if the passed
169 * in node is a microformat, it will still search ancestors for a microformat.
170 *
171 * @param node DOM node to check
172 * @return If the node is contained in a microformat, it returns the parent
173 * DOM node, otherwise returns null
174 */
getParent
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
175 getParent: function(node) {
176 var xpathExpression;
177 var xpathResult;
178
179 xpathExpression = "ancestor::*[";
180 for (let i=0; i < Microformats.list.length; i++) {
181 var mfname = Microformats.list[i];
182 if (i != 0) {
183 xpathExpression += " or ";
184 }
185 if (Microformats[mfname].className) {
186 xpathExpression += "contains(concat(' ', @class, ' '), ' " + Microformats[mfname].className + " ')";
187 } else {
188 var attributeList = Microformats[mfname].attributeValues.split(" ");
189 for (let j=0; j < attributeList.length; j++) {
190 if (j != 0) {
191 xpathExpression += " or ";
192 }
193 xpathExpression += "contains(concat(' ', @" + Microformats[mfname].attributeName + ", ' '), ' " + attributeList[j] + " ')";
194 }
195 }
196 }
197 xpathExpression += "][1]";
198 xpathResult = (node.ownerDocument || node).evaluate(xpathExpression, node, null, Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null);
199 if (xpathResult.singleNodeValue) {
200 xpathResult.singleNodeValue.microformat = mfname;
201 return xpathResult.singleNodeValue;
202 }
203 return null;
204 },
205 /**
206 * If the passed in node is a microformat, this function returns a space
207 * separated list of the microformat names that correspond to this node
208 *
209 * @param node DOM node to check
210 * @return If the node is a microformat, a space separated list of microformat
211 * names, otherwise returns nothing
212 */
getNamesFromNode
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
213 getNamesFromNode: function(node) {
214 var microformatNames = [];
215 var xpathExpression;
216 var xpathResult;
217 for (let i in Microformats)
218 {
219 if (Microformats[i]) {
220 if (Microformats[i].className) {
221 if (Microformats.matchClass(node, Microformats[i].className)) {
222 microformatNames.push(i);
223 continue;
224 }
225 } else if (Microformats[i].attributeValues) {
226 var attribute;
227 if (attribute = node.getAttribute(Microformats[i].attributeName)) {
228 var attributeList = Microformats[i].attributeValues.split(" ");
229 for (let j=0; j < attributeList.length; j++) {
230 /* If we match any attribute, we've got a microformat */
231 if (attribute.match("(^|\\s)" + attributeList[j] + "(\\s|$)")) {
232 microformatNames.push(i);
233 break;
234 }
235 }
236 }
237 }
238 }
239 }
240 return microformatNames.join(" ");
241 },
242 /**
243 * Outputs the contents of a microformat object for debug purposes.
244 *
245 * @param microformatObject JavaScript object that represents a microformat
246 * @return string containing a visual representation of the contents of the microformat
247 */
debug
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
248 debug: function debug(microformatObject) {
dumpObject
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
249 function dumpObject(item, indent)
250 {
251 if (!indent) {
252 indent = "";
253 }
254 var toreturn = "";
255 var testArray = [];
256
257 for (let i in item)
258 {
259 if (testArray[i]) {
260 continue;
261 }
262 if (typeof item[i] == "object") {
263 if ((i != "node") && (i != "resolvedNode")) {
264 if (item[i] && item[i].semanticType) {
265 toreturn += indent + item[i].semanticType + " [" + i + "] { \n";
266 } else {
267 toreturn += indent + "object " + i + " { \n";
268 }
269 toreturn += dumpObject(item[i], indent + "\t");
270 toreturn += indent + "}\n";
271 }
272 } else if ((typeof item[i] != "function") && (i != "semanticType")) {
273 if (item[i]) {
274 toreturn += indent + i + "=" + item[i] + "\n";
275 }
276 }
277 }
278 if (!toreturn && item) {
279 toreturn = item.toString();
280 }
281 return toreturn;
282 }
283 return dumpObject(microformatObject);
284 },
add
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
285 add: function add(microformat, microformatDefinition) {
286 /* We always replace an existing definition with the new one */
287 if (!Microformats[microformat]) {
288 Microformats.list.push(microformat);
289 }
290 Microformats[microformat] = microformatDefinition;
291 microformatDefinition.mfObject.prototype.debug =
anon:292:6
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
292 function(microformatObject) {
293 return Microformats.debug(microformatObject)
294 };
295 },
296 /* All parser specific functions are contained in this object */
297 parser: {
298 /**
299 * Uses the microformat patterns to decide what the correct text for a
300 * given microformat property is. This includes looking at things like
301 * abbr, img/alt, area/alt and value excerpting.
302 *
303 * @param propnode The DOMNode to check
304 * @param parentnode The parent node of the property. If it is a subproperty,
305 * this is the parent property node. If it is not, this is the
306 * microformat node.
307 & @param datatype HTML/text - whether to use innerHTML or innerText - defaults to text
308 * @return A string with the value of the property
309 */
defaultGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
310 defaultGetter: function(propnode, parentnode, datatype) {
311 if (((((propnode.localName.toLowerCase() == "abbr") || (propnode.localName.toLowerCase() == "html:abbr")) && !propnode.namespaceURI) ||
312 ((propnode.localName.toLowerCase() == "abbr") && (propnode.namespaceURI == "http://www.w3.org/1999/xhtml"))) && (propnode.getAttribute("title"))) {
313 return propnode.getAttribute("title");
314 } else if ((propnode.nodeName.toLowerCase() == "img") && (propnode.getAttribute("alt"))) {
315 return propnode.getAttribute("alt");
316 } else if ((propnode.nodeName.toLowerCase() == "area") && (propnode.getAttribute("alt"))) {
317 return propnode.getAttribute("alt");
318 } else if ((propnode.nodeName.toLowerCase() == "textarea") ||
319 (propnode.nodeName.toLowerCase() == "select") ||
320 (propnode.nodeName.toLowerCase() == "input")) {
321 return propnode.value;
322 } else {
323 var values = Microformats.getElementsByClassName(propnode, "value");
324 /* Verify that values are children of the propnode */
325 for (let i = values.length-1; i >= 0; i--) {
326 if (values[i].parentNode != propnode) {
327 values.splice(i,1);
328 }
329 }
330 if (values.length > 0) {
331 var value = "";
332 for (let j=0;j<values.length;j++) {
333 value += Microformats.parser.defaultGetter(values[j], propnode, datatype);
334 }
335 return value;
336 }
337 var s;
338 if (datatype == "HTML") {
339 s = propnode.innerHTML;
340 } else {
341 if (propnode.innerText) {
342 s = propnode.innerText;
343 } else {
344 s = propnode.textContent;
345 }
346 }
347 /* If we are processing a value node, don't remove whitespace */
348 if (!Microformats.matchClass(propnode, "value")) {
349 /* Remove new lines, carriage returns and tabs */
350 s = s.replace(/[\n\r\t]/gi, ' ');
351 /* Replace any double spaces with single spaces */
352 s = s.replace(/\s{2,}/gi, ' ');
353 /* Remove any double spaces that are left */
354 s = s.replace(/\s{2,}/gi, '');
355 /* Remove any spaces at the beginning */
356 s = s.replace(/^\s+/, '');
357 /* Remove any spaces at the end */
358 s = s.replace(/\s+$/, '');
359 }
360 if (s.length > 0) {
361 return s;
362 }
363 }
364 },
365 /**
366 * Used to specifically retrieve a date in a microformat node.
367 * After getting the default text, it normalizes it to an ISO8601 date.
368 *
369 * @param propnode The DOMNode to check
370 * @param parentnode The parent node of the property. If it is a subproperty,
371 * this is the parent property node. If it is not, this is the
372 * microformat node.
373 * @return A string with the normalized date.
374 */
dateTimeGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
375 dateTimeGetter: function(propnode, parentnode) {
376 var date = Microformats.parser.textGetter(propnode, parentnode);
377 if (date) {
378 return Microformats.parser.normalizeISO8601(date);
379 }
380 },
381 /**
382 * Used to specifically retrieve a URI in a microformat node. This includes
383 * looking at an href/img/object/area to get the fully qualified URI.
384 *
385 * @param propnode The DOMNode to check
386 * @param parentnode The parent node of the property. If it is a subproperty,
387 * this is the parent property node. If it is not, this is the
388 * microformat node.
389 * @return A string with the fully qualified URI.
390 */
uriGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
391 uriGetter: function(propnode, parentnode) {
392 var pairs = {"a":"href", "img":"src", "object":"data", "area":"href"};
393 var name = propnode.nodeName.toLowerCase();
394 if (pairs.hasOwnProperty(name)) {
395 return propnode[pairs[name]];
396 }
397 return Microformats.parser.textGetter(propnode, parentnode);
398 },
399 /**
400 * Used to specifically retrieve a telephone number in a microformat node.
401 * Basically this is to handle the face that telephone numbers use value
402 * as the name as one of their subproperties, but value is also used for
403 * value excerpting (http://microformats.org/wiki/hcard#Value_excerpting)
404
405 * @param propnode The DOMNode to check
406 * @param parentnode The parent node of the property. If it is a subproperty,
407 * this is the parent property node. If it is not, this is the
408 * microformat node.
409 * @return A string with the telephone number
410 */
telGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
411 telGetter: function(propnode, parentnode) {
412 var pairs = {"a":"href", "object":"data", "area":"href"};
413 var name = propnode.nodeName.toLowerCase();
414 if (pairs.hasOwnProperty(name)) {
415 var protocol;
416 if (propnode[pairs[name]].indexOf("tel:") == 0) {
417 protocol = "tel:";
418 }
419 if (propnode[pairs[name]].indexOf("fax:") == 0) {
420 protocol = "fax:";
421 }
422 if (propnode[pairs[name]].indexOf("modem:") == 0) {
423 protocol = "modem:";
424 }
425 if (protocol) {
426 if (propnode[pairs[name]].indexOf('?') > 0) {
427 return unescape(propnode[pairs[name]].substring(protocol.length, propnode[pairs[name]].indexOf('?')));
428 } else {
429 return unescape(propnode[pairs[name]].substring(protocol.length));
430 }
431 }
432 }
433 /* Special case - if this node is a value, use the parent node to get all the values */
434 if (Microformats.matchClass(propnode, "value")) {
435 return Microformats.parser.textGetter(parentnode, parentnode);
436 } else {
437 return Microformats.parser.textGetter(propnode, parentnode);
438 }
439 },
440 /**
441 * Used to specifically retrieve an email address in a microformat node.
442 * This includes at an href, as well as removing subject if specified and
443 * the mailto prefix.
444 *
445 * @param propnode The DOMNode to check
446 * @param parentnode The parent node of the property. If it is a subproperty,
447 * this is the parent property node. If it is not, this is the
448 * microformat node.
449 * @return A string with the email address.
450 */
emailGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
451 emailGetter: function(propnode, parentnode) {
452 if ((propnode.nodeName.toLowerCase() == "a") || (propnode.nodeName.toLowerCase() == "area")) {
453 var mailto = propnode.href;
454 /* IO Service won't fully parse mailto, so we do it manually */
455 if (mailto.indexOf('?') > 0) {
456 return unescape(mailto.substring("mailto:".length, mailto.indexOf('?')));
457 } else {
458 return unescape(mailto.substring("mailto:".length));
459 }
460 } else {
461 /* Special case - if this node is a value, use the parent node to get all the values */
462 /* If this case gets executed, per the value design pattern, the result */
463 /* will be the EXACT email address with no extra parsing required */
464 if (Microformats.matchClass(propnode, "value")) {
465 return Microformats.parser.textGetter(parentnode, parentnode);
466 } else {
467 return Microformats.parser.textGetter(propnode, parentnode);
468 }
469 }
470 },
471 /**
472 * Used when a caller needs the text inside a particular DOM node.
473 * It calls defaultGetter to handle all the subtleties of getting
474 * text from a microformat.
475 *
476 * @param propnode The DOMNode to check
477 * @param parentnode The parent node of the property. If it is a subproperty,
478 * this is the parent property node. If it is not, this is the
479 * microformat node.
480 * @return A string with just the text including all tags.
481 */
textGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
482 textGetter: function(propnode, parentnode) {
483 return Microformats.parser.defaultGetter(propnode, parentnode, "text");
484 },
485 /**
486 * Used when a caller needs the HTML inside a particular DOM node.
487 *
488 * @param propnode The DOMNode to check
489 * @param parentnode The parent node of the property. If it is a subproperty,
490 * this is the parent property node. If it is not, this is the
491 * microformat node.
492 * @return An emulated string object that also has a new function called toHTML
493 */
HTMLGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
494 HTMLGetter: function(propnode, parentnode) {
495 /* This is so we can have a string that behaves like a string */
496 /* but also has a new function that can return the HTML that corresponds */
497 /* to the string. */
mfHTML
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
498 function mfHTML(value) {
anon:499:23
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
499 this.valueOf = function() {return value.valueOf();}
anon:500:24
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
500 this.toString = function() {return value.toString();}
501 }
502 mfHTML.prototype = new String;
anon:503:32
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
503 mfHTML.prototype.toHTML = function() {
504 return Microformats.parser.defaultGetter(propnode, parentnode, "HTML");
505 }
506 return new mfHTML(Microformats.parser.defaultGetter(propnode, parentnode, "text"));
507 },
508 /**
509 * Internal parser API used to determine which getter to call based on the
510 * datatype specified in the microformat definition.
511 *
512 * @param prop The microformat property in the definition
513 * @param propnode The DOMNode to check
514 * @param parentnode The parent node of the property. If it is a subproperty,
515 * this is the parent property node. If it is not, this is the
516 * microformat node.
517 * @return A string with the property value.
518 */
datatypeHelper
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
519 datatypeHelper: function(prop, node, parentnode) {
520 var result;
521 var datatype = prop.datatype;
522 if (prop.implied) {
523 datatype = prop.subproperties[prop.implied].datatype;
524 }
525 switch (datatype) {
526 case "dateTime":
527 result = Microformats.parser.dateTimeGetter(node, parentnode);
528 break;
529 case "anyURI":
530 result = Microformats.parser.uriGetter(node, parentnode);
531 break;
532 case "email":
533 result = Microformats.parser.emailGetter(node, parentnode);
534 break;
535 case "tel":
536 result = Microformats.parser.telGetter(node, parentnode);
537 break;
538 case "HTML":
539 result = Microformats.parser.HTMLGetter(node, parentnode);
540 break;
541 case "float":
542 var asText = Microformats.parser.textGetter(node, parentnode);
543 if (!isNaN(asText)) {
544 result = parseFloat(asText);
545 }
546 break;
547 case "custom":
548 result = prop.customGetter(node, parentnode);
549 break;
550 case "microformat":
551 try {
552 result = new Microformats[prop.microformat].mfObject(node);
553 } catch (ex) {
554 /* We can swallow this exception. If the creation of the */
555 /* mf object fails, then the node isn't a microformat */
556 }
557 if (result != undefined) {
558 if (prop.microformat_property) {
559 result = result[prop.microformat_property];
560 }
561 break;
562 }
563 default:
564 result = Microformats.parser.textGetter(node, parentnode);
565 break;
566 }
567 /* This handles the case where one property implies another property */
568 /* For instance, org by itself is actually org.organization-name */
569 if (prop.implied && (result != undefined)) {
570 var temp = result;
571 result = {};
572 result[prop.implied] = temp;
573 }
574 if (prop.values && (result != undefined)) {
575 var validType = false;
576 for (let value in prop.values) {
577 if (result.toLowerCase() == prop.values[value]) {
578 validType = true;
579 break;
580 }
581 }
582 if (!validType) {
583 return;
584 }
585 }
586 return result;
587 },
newMicroformat
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
588 newMicroformat: function(object, in_node, microformat, validate) {
589 /* check to see if we are even valid */
590 if (!Microformats[microformat]) {
591 throw("Invalid microformat - " + microformat);
592 }
593 if (in_node.ownerDocument) {
594 if (Microformats[microformat].attributeName) {
595 if (!(in_node.getAttribute(Microformats[microformat].attributeName))) {
596 throw("Node is not a microformat (" + microformat + ")");
597 }
598 } else {
599 if (!Microformats.matchClass(in_node, Microformats[microformat].className)) {
600 throw("Node is not a microformat (" + microformat + ")");
601 }
602 }
603 }
604 var node = in_node;
605 if ((Microformats[microformat].className) && in_node.ownerDocument) {
606 node = Microformats.parser.preProcessMicroformat(in_node);
607 }
608
609 for (let i in Microformats[microformat].properties) {
610 object.__defineGetter__(i, Microformats.parser.getMicroformatPropertyGenerator(node, microformat, i, object));
611 }
612
613 /* The node in the object should be the original node */
614 object.node = in_node;
615 /* we also store the node that has been "resolved" */
616 object.resolvedNode = node;
617 object.semanticType = microformat;
618 if (validate) {
619 Microformats.parser.validate(node, microformat);
620 }
621 },
getMicroformatPropertyGenerator
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
622 getMicroformatPropertyGenerator: function getMicroformatPropertyGenerator(node, name, property, microformat)
623 {
anon:624:13
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
624 return function() {
625 var result = Microformats.parser.getMicroformatProperty(node, name, property);
626 // delete microformat[property];
627 // microformat[property] = result;
628 return result;
629 };
630 },
getPropertyInternal
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
631 getPropertyInternal: function getPropertyInternal(propnode, parentnode, propobj, propname, mfnode) {
632 var result;
633 if (propobj.subproperties) {
634 for (let subpropname in propobj.subproperties) {
635 var subpropnodes;
636 var subpropobj = propobj.subproperties[subpropname];
637 if (subpropobj.rel == true) {
638 subpropnodes = Microformats.getElementsByAttribute(propnode, "rel", subpropname);
639 } else {
640 subpropnodes = Microformats.getElementsByClassName(propnode, subpropname);
641 }
642 var resultArray = [];
643 var subresult;
644 for (let i = 0; i < subpropnodes.length; i++) {
645 subresult = Microformats.parser.getPropertyInternal(subpropnodes[i], propnode,
646 subpropobj,
647 subpropname, mfnode);
648 if (subresult != undefined) {
649 resultArray.push(subresult);
650 /* If we're not a plural property, don't bother getting more */
651 if (!subpropobj.plural) {
652 break;
653 }
654 }
655 }
656 if (resultArray.length == 0) {
657 subresult = Microformats.parser.getPropertyInternal(propnode, null,
658 subpropobj,
659 subpropname, mfnode);
660 if (subresult != undefined) {
661 resultArray.push(subresult);
662 }
663 }
664 if (resultArray.length > 0) {
665 result = result || {};
666 if (subpropobj.plural) {
667 result[subpropname] = resultArray;
668 } else {
669 result[subpropname] = resultArray[0];
670 }
671 }
672 }
673 }
674 if (!parentnode || (!result && propobj.subproperties)) {
675 if (propobj.virtual) {
676 if (propobj.virtualGetter) {
677 result = propobj.virtualGetter(mfnode || propnode);
678 } else {
679 result = Microformats.parser.datatypeHelper(propobj, propnode);
680 }
681 } else if (propobj.implied) {
682 result = Microformats.parser.datatypeHelper(propobj, propnode);
683 }
684 } else if (!result) {
685 result = Microformats.parser.datatypeHelper(propobj, propnode, parentnode);
686 }
687 return result;
688 },
getMicroformatProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
689 getMicroformatProperty: function getMicroformatProperty(in_mfnode, mfname, propname) {
690 var mfnode = in_mfnode;
691 /* If the node has not been preprocessed, the requested microformat */
692 /* is a class based microformat and the passed in node is not the */
693 /* entire document, preprocess it. Preprocessing the node involves */
694 /* creating a duplicate of the node and taking care of things like */
695 /* the include and header design patterns */
696 if (!in_mfnode.origNode && Microformats[mfname].className && in_mfnode.ownerDocument) {
697 mfnode = Microformats.parser.preProcessMicroformat(in_mfnode);
698 }
699 /* propobj is the corresponding property object in the microformat */
700 var propobj;
701 /* If there is a corresponding property in the microformat, use it */
702 if (Microformats[mfname].properties[propname]) {
703 propobj = Microformats[mfname].properties[propname];
704 } else {
705 /* If we didn't get a property, bail */
706 return;
707 }
708 /* Query the correct set of nodes (rel or class) based on the setting */
709 /* in the property */
710 var propnodes;
711 if (propobj.rel == true) {
712 propnodes = Microformats.getElementsByAttribute(mfnode, "rel", propname);
713 } else {
714 propnodes = Microformats.getElementsByClassName(mfnode, propname);
715 }
716 for (let i=propnodes.length-1; i >= 0; i--) {
717 /* The reason getParent is not used here is because this code does */
718 /* not apply to attribute based microformats, plus adr and geo */
719 /* when contained in hCard are a special case */
720 var parentnode;
721 var node = propnodes[i];
722 var xpathExpression = "";
723 for (let j=0; j < Microformats.list.length; j++) {
724 /* Don't treat adr or geo in an hCard as a microformat in this case */
725 if ((mfname == "hCard") && ((Microformats.list[j] == "adr") || (Microformats.list[j] == "geo"))) {
726 continue;
727 }
728 if (Microformats[Microformats.list[j]].className) {
729 if (xpathExpression.length == 0) {
730 xpathExpression = "ancestor::*[";
731 } else {
732 xpathExpression += " or ";
733 }
734 xpathExpression += "contains(concat(' ', @class, ' '), ' " + Microformats[Microformats.list[j]].className + " ')";
735 }
736 }
737 xpathExpression += "][1]";
738 var xpathResult = (node.ownerDocument || node).evaluate(xpathExpression, node, null, Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null);
739 if (xpathResult.singleNodeValue) {
740 xpathResult.singleNodeValue.microformat = mfname;
741 parentnode = xpathResult.singleNodeValue;
742 }
743 /* If the propnode is not a child of the microformat, and */
744 /* the property belongs to the parent microformat as well, */
745 /* remove it. */
746 if (parentnode != mfnode) {
747 var mfNameString = Microformats.getNamesFromNode(parentnode);
748 var mfNames = mfNameString.split(" ");
749 var j;
750 for (j=0; j < mfNames.length; j++) {
751 /* If this property is in the parent microformat, remove the node */
752 if (Microformats[mfNames[j]].properties[propname]) {
753 propnodes.splice(i,1);;
754 break;
755 }
756 }
757 }
758 }
759 if (propnodes.length > 0) {
760 var resultArray = [];
761 for (let i = 0; i < propnodes.length; i++) {
762 var subresult = Microformats.parser.getPropertyInternal(propnodes[i],
763 mfnode,
764 propobj,
765 propname);
766 if (subresult != undefined) {
767 resultArray.push(subresult);
768 /* If we're not a plural property, don't bother getting more */
769 if (!propobj.plural) {
770 return resultArray[0];
771 }
772 }
773 }
774 if (resultArray.length > 0) {
775 return resultArray;
776 }
777 } else {
778 /* If we didn't find any class nodes, check to see if this property */
779 /* is virtual and if so, call getPropertyInternal again */
780 if (propobj.virtual) {
781 return Microformats.parser.getPropertyInternal(mfnode, null,
782 propobj, propname);
783 }
784 }
785 return;
786 },
787 /**
788 * Internal parser API used to resolve includes and headers. Includes are
789 * resolved by simply cloning the node and replacing it in a clone of the
790 * original DOM node. Headers are resolved by creating a span and then copying
791 * the innerHTML and the class name.
792 *
793 * @param in_mfnode The node to preProcess.
794 * @return If the node had includes or headers, a cloned node otherwise
795 * the original node. You can check to see if the node was cloned
796 * by looking for .origNode in the new node.
797 */
preProcessMicroformat
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
798 preProcessMicroformat: function preProcessMicroformat(in_mfnode) {
799 var mfnode;
800 var includes = Microformats.getElementsByClassName(in_mfnode, "include");
801 if ((includes.length > 0) || ((in_mfnode.nodeName.toLowerCase() == "td") && (in_mfnode.getAttribute("headers")))) {
802 mfnode = in_mfnode.cloneNode(true);
803 mfnode.origNode = in_mfnode;
804 if (includes.length > 0) {
805 includes = Microformats.getElementsByClassName(mfnode, "include");
806 var includeId;
807 var include_length = includes.length;
808 for (let i = include_length -1; i >= 0; i--) {
809 if (includes[i].nodeName.toLowerCase() == "a") {
810 includeId = includes[i].getAttribute("href").substr(1);
811 }
812 if (includes[i].nodeName.toLowerCase() == "object") {
813 includeId = includes[i].getAttribute("data").substr(1);
814 }
815 if (in_mfnode.ownerDocument.getElementById(includeId)) {
816 includes[i].parentNode.replaceChild(in_mfnode.ownerDocument.getElementById(includeId).cloneNode(true), includes[i]);
817 }
818 }
819 } else {
820 var headers = in_mfnode.getAttribute("headers").split(" ");
821 for (let i = 0; i < headers.length; i++) {
822 var tempNode = in_mfnode.ownerDocument.createElement("span");
823 var headerNode = in_mfnode.ownerDocument.getElementById(headers[i]);
824 if (headerNode) {
825 tempNode.innerHTML = headerNode.innerHTML;
826 tempNode.className = headerNode.className;
827 mfnode.appendChild(tempNode);
828 }
829 }
830 }
831 } else {
832 mfnode = in_mfnode;
833 }
834 return mfnode;
835 },
validate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
836 validate: function validate(mfnode, mfname) {
837 var error = "";
838 if (Microformats[mfname].validate) {
839 return Microformats[mfname].validate(mfnode);
840 } else if (Microformats[mfname].required) {
841 for (let i=0;i<Microformats[mfname].required.length;i++) {
842 if (!Microformats.parser.getMicroformatProperty(mfnode, mfname, Microformats[mfname].required[i])) {
843 error += "Required property " + Microformats[mfname].required[i] + " not specified\n";
844 }
845 }
846 if (error.length > 0) {
847 throw(error);
848 }
849 return true;
850 }
851 },
852 /* This function normalizes an ISO8601 date by adding punctuation and */
853 /* ensuring that hours and seconds have values */
normalizeISO8601
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
854 normalizeISO8601: function normalizeISO8601(string)
855 {
856 var dateArray = string.match(/(\d\d\d\d)(?:-?(\d\d)(?:-?(\d\d)(?:[T ](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(?:([-+Z])(?:(\d\d)(?::?(\d\d))?)?)?)?)?)?/);
857
858 var dateString;
859 var tzOffset = 0;
860 if (!dateArray) {
861 return;
862 }
863 if (dateArray[1]) {
864 dateString = dateArray[1];
865 if (dateArray[2]) {
866 dateString += "-" + dateArray[2];
867 if (dateArray[3]) {
868 dateString += "-" + dateArray[3];
869 if (dateArray[4]) {
870 dateString += "T" + dateArray[4];
871 if (dateArray[5]) {
872 dateString += ":" + dateArray[5];
873 } else {
874 dateString += ":" + "00";
875 }
876 if (dateArray[6]) {
877 dateString += ":" + dateArray[6];
878 } else {
879 dateString += ":" + "00";
880 }
881 if (dateArray[7]) {
882 dateString += "." + dateArray[7];
883 }
884 if (dateArray[8]) {
885 dateString += dateArray[8];
886 if ((dateArray[8] == "+") || (dateArray[8] == "-")) {
887 if (dateArray[9]) {
888 dateString += dateArray[9];
889 if (dateArray[10]) {
890 dateString += dateArray[10];
891 }
892 }
893 }
894 }
895 }
896 }
897 }
898 }
899 return dateString;
900 }
901 },
902 /**
903 * Converts an ISO8601 date into a JavaScript date object, honoring the TZ
904 * offset and Z if present to convert the date to local time
905 * NOTE: I'm using an extra parameter on the date object for this function.
906 * I set date.time to true if there is a date, otherwise date.time is false.
907 *
908 * @param string ISO8601 formatted date
909 * @return JavaScript date object that represents the ISO date.
910 */
dateFromISO8601
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
911 dateFromISO8601: function dateFromISO8601(string) {
912 var dateArray = string.match(/(\d\d\d\d)(?:-?(\d\d)(?:-?(\d\d)(?:[T ](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(?:([-+Z])(?:(\d\d)(?::?(\d\d))?)?)?)?)?)?/);
913
914 var date = new Date(dateArray[1], 0, 1);
915 date.time = false;
916
917 if (dateArray[2]) {
918 date.setMonth(dateArray[2] - 1);
919 }
920 if (dateArray[3]) {
921 date.setDate(dateArray[3]);
922 }
923 if (dateArray[4]) {
924 date.setHours(dateArray[4]);
925 date.time = true;
926 if (dateArray[5]) {
927 date.setMinutes(dateArray[5]);
928 if (dateArray[6]) {
929 date.setSeconds(dateArray[6]);
930 if (dateArray[7]) {
931 date.setMilliseconds(Number("0." + dateArray[7]) * 1000);
932 }
933 }
934 }
935 }
936 if (dateArray[8]) {
937 if (dateArray[8] == "-") {
938 if (dateArray[9] && dateArray[10]) {
939 date.setHours(date.getHours() + parseInt(dateArray[9], 10));
940 date.setMinutes(date.getMinutes() + parseInt(dateArray[10], 10));
941 }
942 } else if (dateArray[8] == "+") {
943 if (dateArray[9] && dateArray[10]) {
944 date.setHours(date.getHours() - parseInt(dateArray[9], 10));
945 date.setMinutes(date.getMinutes() - parseInt(dateArray[10], 10));
946 }
947 }
948 /* at this point we have the time in gmt */
949 /* convert to local if we had a Z - or + */
950 if (dateArray[8]) {
951 var tzOffset = date.getTimezoneOffset();
952 if (tzOffset < 0) {
953 date.setMinutes(date.getMinutes() + tzOffset);
954 } else if (tzOffset > 0) {
955 date.setMinutes(date.getMinutes() - tzOffset);
956 }
957 }
958 }
959 return date;
960 },
961 /**
962 * Converts a Javascript date object into an ISO 8601 formatted date
963 * NOTE: I'm using an extra parameter on the date object for this function.
964 * If date.time is NOT true, this function only outputs the date.
965 *
966 * @param date Javascript Date object
967 * @param punctuation true if the date should have -/:
968 * @return string with the ISO date.
969 */
iso8601FromDate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
970 iso8601FromDate: function iso8601FromDate(date, punctuation) {
971 var string = date.getFullYear().toString();
972 if (punctuation) {
973 string += "-";
974 }
975 string += (date.getMonth() + 1).toString().replace(/\b(\d)\b/g, '0$1');
976 if (punctuation) {
977 string += "-";
978 }
979 string += date.getDate().toString().replace(/\b(\d)\b/g, '0$1');
980 if (date.time) {
981 string += "T";
982 string += date.getHours().toString().replace(/\b(\d)\b/g, '0$1');
983 if (punctuation) {
984 string += ":";
985 }
986 string += date.getMinutes().toString().replace(/\b(\d)\b/g, '0$1');
987 if (punctuation) {
988 string += ":";
989 }
990 string += date.getSeconds().toString().replace(/\b(\d)\b/g, '0$1');
991 if (date.getMilliseconds() > 0) {
992 if (punctuation) {
993 string += ".";
994 }
995 string += date.getMilliseconds().toString();
996 }
997 }
998 return string;
999 },
simpleEscape
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1000 simpleEscape: function simpleEscape(s)
1001 {
1002 s = s.replace(/\&/g, '%26');
1003 s = s.replace(/\#/g, '%23');
1004 s = s.replace(/\+/g, '%2B');
1005 s = s.replace(/\-/g, '%2D');
1006 s = s.replace(/\=/g, '%3D');
1007 s = s.replace(/\'/g, '%27');
1008 s = s.replace(/\,/g, '%2C');
1009 // s = s.replace(/\r/g, '%0D');
1010 // s = s.replace(/\n/g, '%0A');
1011 s = s.replace(/ /g, '+');
1012 return s;
1013 },
1014 /**
1015 * Not intended for external consumption. Microformat implementations might use it.
1016 *
1017 * Retrieve elements matching all classes listed in a space-separated string.
1018 * I had to implement my own because I need an Array, not an nsIDomNodeList
1019 *
1020 * @param rootElement The DOM element at which to start searching (optional)
1021 * @param className A space separated list of classenames
1022 * @return microformatNodes An array of DOM Nodes, each representing a
1023 microformat in the document.
1024 */
getElementsByClassName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1025 getElementsByClassName: function getElementsByClassName(rootNode, className)
1026 {
1027 var returnElements = [];
1028
1029 if ((rootNode.ownerDocument || rootNode).getElementsByClassName) {
1030 /* Firefox 3 - native getElementsByClassName */
1031 var col = rootNode.getElementsByClassName(className);
1032 for (let i = 0; i < col.length; i++) {
1033 returnElements[i] = col[i];
1034 }
1035 } else if ((rootNode.ownerDocument || rootNode).evaluate) {
1036 /* Firefox 2 and below - XPath */
1037 var xpathExpression;
1038 xpathExpression = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1039 var xpathResult = (rootNode.ownerDocument || rootNode).evaluate(xpathExpression, rootNode, null, 0, null);
1040
1041 var node;
1042 while (node = xpathResult.iterateNext()) {
1043 returnElements.push(node);
1044 }
1045 } else {
1046 /* Slow fallback for testing */
1047 className = className.replace(/\-/g, "\\-");
1048 var elements = rootNode.getElementsByTagName("*");
1049 for (let i=0;i<elements.length;i++) {
1050 if (elements[i].className.match("(^|\\s)" + className + "(\\s|$)")) {
1051 returnElements.push(elements[i]);
1052 }
1053 }
1054 }
1055 return returnElements;
1056 },
1057 /**
1058 * Not intended for external consumption. Microformat implementations might use it.
1059 *
1060 * Retrieve elements matching an attribute and an attribute list in a space-separated string.
1061 *
1062 * @param rootElement The DOM element at which to start searching (optional)
1063 * @param atributeName The attribute name to match against
1064 * @param attributeValues A space separated list of attribute values
1065 * @return microformatNodes An array of DOM Nodes, each representing a
1066 microformat in the document.
1067 */
getElementsByAttribute
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1068 getElementsByAttribute: function getElementsByAttribute(rootNode, attributeName, attributeValues)
1069 {
1070 var attributeList = attributeValues.split(" ");
1071
1072 var returnElements = [];
1073
1074 if ((rootNode.ownerDocument || rootNode).evaluate) {
1075 /* Firefox 3 and below - XPath */
1076 /* Create an XPath expression based on the attribute list */
1077 var xpathExpression = ".//*[";
1078 for (let i = 0; i < attributeList.length; i++) {
1079 if (i != 0) {
1080 xpathExpression += " or ";
1081 }
1082 xpathExpression += "contains(concat(' ', @" + attributeName + ", ' '), ' " + attributeList[i] + " ')";
1083 }
1084 xpathExpression += "]";
1085
1086 var xpathResult = (rootNode.ownerDocument || rootNode).evaluate(xpathExpression, rootNode, null, 0, null);
1087
1088 var node;
1089 while (node = xpathResult.iterateNext()) {
1090 returnElements.push(node);
1091 }
1092 } else {
1093 /* Need Slow fallback for testing */
1094 }
1095 return returnElements;
1096 },
matchClass
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1097 matchClass: function matchClass(node, className) {
1098 var classValue = node.getAttribute("class");
1099 return (classValue && classValue.match("(^|\\s)" + className + "(\\s|$)"));
1100 }
1101 };
1102
1103 /* MICROFORMAT DEFINITIONS BEGIN HERE */
1104
adr
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1105 function adr(node, validate) {
1106 if (node) {
1107 Microformats.parser.newMicroformat(this, node, "adr", validate);
1108 }
1109 }
1110
toString
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1111 adr.prototype.toString = function() {
1112 var address_text = "";
1113 var start_parens = false;
1114 if (this["street-address"]) {
1115 address_text += this["street-address"][0];
1116 } else if (this["extended-address"]) {
1117 address_text += this["extended-address"];
1118 }
1119 if (this["locality"]) {
1120 if (this["street-address"] || this["extended-address"]) {
1121 address_text += " (";
1122 start_parens = true;
1123 }
1124 address_text += this["locality"];
1125 }
1126 if (this["region"]) {
1127 if ((this["street-address"] || this["extended-address"]) && (!start_parens)) {
1128 address_text += " (";
1129 start_parens = true;
1130 } else if (this["locality"]) {
1131 address_text += ", ";
1132 }
1133 address_text += this["region"];
1134 }
1135 if (this["country-name"]) {
1136 if ((this["street-address"] || this["extended-address"]) && (!start_parens)) {
1137 address_text += " (";
1138 start_parens = true;
1139 address_text += this["country-name"];
1140 } else if ((!this["locality"]) && (!this["region"])) {
1141 address_text += this["country-name"];
1142 } else if (((!this["locality"]) && (this["region"])) || ((this["locality"]) && (!this["region"]))) {
1143 address_text += ", ";
1144 address_text += this["country-name"];
1145 }
1146 }
1147 if (start_parens) {
1148 address_text += ")";
1149 }
1150 return address_text;
1151 }
1152
1153 var adr_definition = {
1154 mfObject: adr,
1155 className: "adr",
1156 properties: {
1157 "type" : {
1158 plural: true,
1159 values: ["work", "home", "pref", "postal", "dom", "intl", "parcel"]
1160 },
1161 "post-office-box" : {
1162 },
1163 "street-address" : {
1164 plural: true
1165 },
1166 "extended-address" : {
1167 },
1168 "locality" : {
1169 },
1170 "region" : {
1171 },
1172 "postal-code" : {
1173 },
1174 "country-name" : {
1175 }
1176 },
validate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1177 validate: function(node) {
1178 var xpathExpression = "count(descendant::*[" +
1179 "contains(concat(' ', @class, ' '), ' post-office-box ')" +
1180 " or contains(concat(' ', @class, ' '), ' street-address ')" +
1181 " or contains(concat(' ', @class, ' '), ' extended-address ')" +
1182 " or contains(concat(' ', @class, ' '), ' locality ')" +
1183 " or contains(concat(' ', @class, ' '), ' region ')" +
1184 " or contains(concat(' ', @class, ' '), ' postal-code ')" +
1185 " or contains(concat(' ', @class, ' '), ' country-name')" +
1186 "])";
1187 var xpathResult = (node.ownerDocument || node).evaluate(xpathExpression, node, null, Components.interfaces.nsIDOMXPathResult.ANY_TYPE, null).numberValue;
1188 if (xpathResult == 0) {
1189 throw("Unable to create microformat");
1190 }
1191 return true;
1192 }
1193 };
1194
1195 Microformats.add("adr", adr_definition);
1196
hCard
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1197 function hCard(node, validate) {
1198 if (node) {
1199 Microformats.parser.newMicroformat(this, node, "hCard", validate);
1200 }
1201 }
toString
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1202 hCard.prototype.toString = function() {
1203 if (this.resolvedNode) {
1204 /* If this microformat has an include pattern, put the */
1205 /* organization-name in parenthesis after the fn to differentiate */
1206 /* them. */
1207 var fns = Microformats.getElementsByClassName(this.node, "fn");
1208 if (fns.length === 0) {
1209 if (this.fn) {
1210 if (this.org && this.org[0]["organization-name"] && (this.fn != this.org[0]["organization-name"])) {
1211 return this.fn + " (" + this.org[0]["organization-name"] + ")";
1212 }
1213 }
1214 }
1215 }
1216 return this.fn;
1217 }
1218
1219 var hCard_definition = {
1220 mfObject: hCard,
1221 className: "vcard",
1222 required: ["fn"],
1223 properties: {
1224 "adr" : {
1225 plural: true,
1226 datatype: "microformat",
1227 microformat: "adr"
1228 },
1229 "agent" : {
1230 plural: true,
1231 datatype: "microformat",
1232 microformat: "hCard"
1233 },
1234 "bday" : {
1235 datatype: "dateTime"
1236 },
1237 "class" : {
1238 },
1239 "category" : {
1240 plural: true,
1241 datatype: "microformat",
1242 microformat: "tag",
1243 microformat_property: "tag"
1244 },
1245 "email" : {
1246 subproperties: {
1247 "type" : {
1248 plural: true,
1249 values: ["internet", "x400", "pref"]
1250 },
1251 "value" : {
1252 datatype: "email",
1253 virtual: true
1254 }
1255 },
1256 plural: true
1257 },
1258 "fn" : {
1259 required: true
1260 },
1261 "geo" : {
1262 datatype: "microformat",
1263 microformat: "geo"
1264 },
1265 "key" : {
1266 plural: true
1267 },
1268 "label" : {
1269 plural: true
1270 },
1271 "logo" : {
1272 plural: true,
1273 datatype: "anyURI"
1274 },
1275 "mailer" : {
1276 plural: true
1277 },
1278 "n" : {
1279 subproperties: {
1280 "honorific-prefix" : {
1281 plural: true
1282 },
1283 "given-name" : {
1284 plural: true
1285 },
1286 "additional-name" : {
1287 plural: true
1288 },
1289 "family-name" : {
1290 plural: true
1291 },
1292 "honorific-suffix" : {
1293 plural: true
1294 }
1295 },
1296 virtual: true,
1297 /* Implied "n" Optimization */
1298 /* http://microformats.org/wiki/hcard#Implied_.22n.22_Optimization */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1299 virtualGetter: function(mfnode) {
1300 var fn = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "fn");
1301 var orgs = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "org");
1302 var given_name = [];
1303 var family_name = [];
1304 if (fn && (!orgs || (orgs.length > 1) || (fn != orgs[0]["organization-name"]))) {
1305 var fns = fn.split(" ");
1306 if (fns.length === 2) {
1307 if (fns[0].charAt(fns[0].length-1) == ',') {
1308 given_name[0] = fns[1];
1309 family_name[0] = fns[0].substr(0, fns[0].length-1);
1310 } else if (fns[1].length == 1) {
1311 given_name[0] = fns[1];
1312 family_name[0] = fns[0];
1313 } else if ((fns[1].length == 2) && (fns[1].charAt(fns[1].length-1) == '.')) {
1314 given_name[0] = fns[1];
1315 family_name[0] = fns[0];
1316 } else {
1317 given_name[0] = fns[0];
1318 family_name[0] = fns[1];
1319 }
1320 return {"given-name" : given_name, "family-name" : family_name};
1321 }
1322 }
1323 }
1324 },
1325 "nickname" : {
1326 plural: true,
1327 virtual: true,
1328 /* Implied "nickname" Optimization */
1329 /* http://microformats.org/wiki/hcard#Implied_.22nickname.22_Optimization */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1330 virtualGetter: function(mfnode) {
1331 var fn = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "fn");
1332 var orgs = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "org");
1333 var given_name;
1334 var family_name;
1335 if (fn && (!orgs || (orgs.length) > 1 || (fn != orgs[0]["organization-name"]))) {
1336 var fns = fn.split(" ");
1337 if (fns.length === 1) {
1338 return [fns[0]];
1339 }
1340 }
1341 return;
1342 }
1343 },
1344 "note" : {
1345 plural: true,
1346 datatype: "HTML"
1347 },
1348 "org" : {
1349 subproperties: {
1350 "organization-name" : {
1351 },
1352 "organization-unit" : {
1353 plural: true
1354 }
1355 },
1356 plural: true,
1357 implied: "organization-name"
1358 },
1359 "photo" : {
1360 plural: true,
1361 datatype: "anyURI"
1362 },
1363 "rev" : {
1364 datatype: "dateTime"
1365 },
1366 "role" : {
1367 plural: true
1368 },
1369 "sequence" : {
1370 },
1371 "sort-string" : {
1372 },
1373 "sound" : {
1374 plural: true
1375 },
1376 "title" : {
1377 plural: true
1378 },
1379 "tel" : {
1380 subproperties: {
1381 "type" : {
1382 plural: true,
1383 values: ["msg", "home", "work", "pref", "voice", "fax", "cell", "video", "pager", "bbs", "car", "isdn", "pcs"]
1384 },
1385 "value" : {
1386 datatype: "tel"
1387 }
1388 },
1389 plural: true,
1390 implied: "value"
1391 },
1392 "tz" : {
1393 },
1394 "uid" : {
1395 datatype: "anyURI"
1396 },
1397 "url" : {
1398 plural: true,
1399 datatype: "anyURI"
1400 }
1401 }
1402 };
1403
1404 Microformats.add("hCard", hCard_definition);
1405
hCalendar
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1406 function hCalendar(node, validate) {
1407 if (node) {
1408 Microformats.parser.newMicroformat(this, node, "hCalendar", validate);
1409 }
1410 }
toString
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1411 hCalendar.prototype.toString = function() {
1412 if (this.resolvedNode) {
1413 /* If this microformat has an include pattern, put the */
1414 /* dtstart in parenthesis after the summary to differentiate */
1415 /* them. */
1416 var summaries = Microformats.getElementsByClassName(this.node, "summary");
1417 if (summaries.length === 0) {
1418 if (this.summary) {
1419 if (this.dtstart) {
1420 return this.summary + " (" + Microformats.dateFromISO8601(this.dtstart).toLocaleString() + ")";
1421 }
1422 }
1423 }
1424 }
1425 if (this.dtstart) {
1426 return this.summary;
1427 }
1428 return;
1429 }
1430
1431 var hCalendar_definition = {
1432 mfObject: hCalendar,
1433 className: "vevent",
1434 required: ["summary", "dtstart"],
1435 properties: {
1436 "category" : {
1437 plural: true,
1438 datatype: "microformat",
1439 microformat: "tag",
1440 microformat_property: "tag"
1441 },
1442 "class" : {
1443 values: ["public", "private", "confidential"]
1444 },
1445 "description" : {
1446 datatype: "HTML"
1447 },
1448 "dtstart" : {
1449 datatype: "dateTime"
1450 },
1451 "dtend" : {
1452 datatype: "dateTime"
1453 },
1454 "dtstamp" : {
1455 datatype: "dateTime"
1456 },
1457 "duration" : {
1458 },
1459 "geo" : {
1460 datatype: "microformat",
1461 microformat: "geo"
1462 },
1463 "location" : {
1464 datatype: "microformat",
1465 microformat: "hCard"
1466 },
1467 "status" : {
1468 values: ["tentative", "confirmed", "cancelled"]
1469 },
1470 "summary" : {},
1471 "transp" : {
1472 values: ["opaque", "transparent"]
1473 },
1474 "uid" : {
1475 datatype: "anyURI"
1476 },
1477 "url" : {
1478 datatype: "anyURI"
1479 },
1480 "last-modified" : {
1481 datatype: "dateTime"
1482 },
1483 "rrule" : {
1484 subproperties: {
1485 "interval" : {
1486 virtual: true,
1487 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1488 virtualGetter: function(mfnode) {
1489 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "interval");
1490 }
1491 },
1492 "freq" : {
1493 virtual: true,
1494 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1495 virtualGetter: function(mfnode) {
1496 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "freq");
1497 }
1498 },
1499 "bysecond" : {
1500 virtual: true,
1501 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1502 virtualGetter: function(mfnode) {
1503 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "bysecond");
1504 }
1505 },
1506 "byminute" : {
1507 virtual: true,
1508 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1509 virtualGetter: function(mfnode) {
1510 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byminute");
1511 }
1512 },
1513 "byhour" : {
1514 virtual: true,
1515 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1516 virtualGetter: function(mfnode) {
1517 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byhour");
1518 }
1519 },
1520 "bymonthday" : {
1521 virtual: true,
1522 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1523 virtualGetter: function(mfnode) {
1524 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "bymonthday");
1525 }
1526 },
1527 "byyearday" : {
1528 virtual: true,
1529 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1530 virtualGetter: function(mfnode) {
1531 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byyearday");
1532 }
1533 },
1534 "byweekno" : {
1535 virtual: true,
1536 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1537 virtualGetter: function(mfnode) {
1538 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byweekno");
1539 }
1540 },
1541 "bymonth" : {
1542 virtual: true,
1543 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1544 virtualGetter: function(mfnode) {
1545 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "bymonth");
1546 }
1547 },
1548 "byday" : {
1549 virtual: true,
1550 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1551 virtualGetter: function(mfnode) {
1552 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byday");
1553 }
1554 },
1555 "until" : {
1556 virtual: true,
1557 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1558 virtualGetter: function(mfnode) {
1559 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "until");
1560 }
1561 },
1562 "count" : {
1563 virtual: true,
1564 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1565 virtualGetter: function(mfnode) {
1566 return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "count");
1567 }
1568 }
1569 },
retrieve
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1570 retrieve: function(mfnode, property) {
1571 var value = Microformats.parser.textGetter(mfnode);
1572 var rrule;
1573 rrule = value.split(';');
1574 for (let i=0; i < rrule.length; i++) {
1575 if (rrule[i].match(property)) {
1576 return rrule[i].split('=')[1];
1577 }
1578 }
1579 }
1580 }
1581 }
1582 };
1583
1584 Microformats.add("hCalendar", hCalendar_definition);
1585
geo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1586 function geo(node, validate) {
1587 if (node) {
1588 Microformats.parser.newMicroformat(this, node, "geo", validate);
1589 }
1590 }
toString
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1591 geo.prototype.toString = function() {
1592 if (this.latitude != undefined) {
1593 if (!isFinite(this.latitude) || (this.latitude > 360) || (this.latitude < -360)) {
1594 return;
1595 }
1596 }
1597 if (this.longitude != undefined) {
1598 if (!isFinite(this.longitude) || (this.longitude > 360) || (this.longitude < -360)) {
1599 return;
1600 }
1601 }
1602
1603 if ((this.latitude != undefined) && (this.longitude != undefined)) {
1604 var s;
1605 if ((this.node.localName.toLowerCase() == "abbr") || (this.node.localName.toLowerCase() == "html:abbr")) {
1606 s = this.node.textContent;
1607 }
1608
1609 if (s) {
1610 return s;
1611 }
1612
1613 /* check if geo is contained in a vcard */
1614 var xpathExpression = "ancestor::*[contains(concat(' ', @class, ' '), ' vcard ')]";
1615 var xpathResult = this.node.ownerDocument.evaluate(xpathExpression, this.node, null, Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null);
1616 if (xpathResult.singleNodeValue) {
1617 var hcard = new hCard(xpathResult.singleNodeValue);
1618 if (hcard.fn) {
1619 return hcard.fn;
1620 }
1621 }
1622 /* check if geo is contained in a vevent */
1623 xpathExpression = "ancestor::*[contains(concat(' ', @class, ' '), ' vevent ')]";
1624 xpathResult = this.node.ownerDocument.evaluate(xpathExpression, this.node, null, Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, xpathResult);
1625 if (xpathResult.singleNodeValue) {
1626 var hcal = new hCalendar(xpathResult.singleNodeValue);
1627 if (hcal.summary) {
1628 return hcal.summary;
1629 }
1630 }
1631 if (s) {
1632 return s;
1633 } else {
1634 return this.latitude + ", " + this.longitude;
1635 }
1636 }
1637 }
1638
1639 var geo_definition = {
1640 mfObject: geo,
1641 className: "geo",
1642 required: ["latitude","longitude"],
1643 properties: {
1644 "latitude" : {
1645 datatype: "float",
1646 virtual: true,
1647 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1648 virtualGetter: function(mfnode) {
1649 var value = Microformats.parser.textGetter(mfnode);
1650 var latlong;
1651 if (value.match(';')) {
1652 latlong = value.split(';');
1653 if (latlong[0]) {
1654 if (!isNaN(latlong[0])) {
1655 return parseFloat(latlong[0]);
1656 }
1657 }
1658 }
1659 }
1660 },
1661 "longitude" : {
1662 datatype: "float",
1663 virtual: true,
1664 /* This will only be called in the virtual case */
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1665 virtualGetter: function(mfnode) {
1666 var value = Microformats.parser.textGetter(mfnode);
1667 var latlong;
1668 if (value.match(';')) {
1669 latlong = value.split(';');
1670 if (latlong[1]) {
1671 if (!isNaN(latlong[1])) {
1672 return parseFloat(latlong[1]);
1673 }
1674 }
1675 }
1676 }
1677 }
1678 },
validate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1679 validate: function(node) {
1680 var latitude = Microformats.parser.getMicroformatProperty(node, "geo", "latitude");
1681 var longitude = Microformats.parser.getMicroformatProperty(node, "geo", "longitude");
1682 if (latitude != undefined) {
1683 if (!isFinite(latitude) || (latitude > 360) || (latitude < -360)) {
1684 throw("Invalid latitude");
1685 }
1686 } else {
1687 throw("No latitude specified");
1688 }
1689 if (longitude != undefined) {
1690 if (!isFinite(longitude) || (longitude > 360) || (longitude < -360)) {
1691 throw("Invalid longitude");
1692 }
1693 } else {
1694 throw("No longitude specified");
1695 }
1696 return true;
1697 }
1698 };
1699
1700 Microformats.add("geo", geo_definition);
1701
tag
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1702 function tag(node, validate) {
1703 if (node) {
1704 Microformats.parser.newMicroformat(this, node, "tag", validate);
1705 }
1706 }
toString
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1707 tag.prototype.toString = function() {
1708 return this.tag;
1709 }
1710
1711 var tag_definition = {
1712 mfObject: tag,
1713 attributeName: "rel",
1714 attributeValues: "tag",
1715 properties: {
1716 "tag" : {
1717 virtual: true,
virtualGetter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1718 virtualGetter: function(mfnode) {
1719 if (mfnode.href) {
1720 var ioService = Components.classes["@mozilla.org/network/io-service;1"].
1721 getService(Components.interfaces.nsIIOService);
1722 var uri = ioService.newURI(mfnode.href, null, null);
1723 var url_array = uri.path.split("/");
1724 for(let i=url_array.length-1; i > 0; i--) {
1725 if (url_array[i] !== "") {
1726 var tag
1727 if (tag = Microformats.tag.validTagName(url_array[i].replace(/\+/g, ' '))) {
1728 try {
1729 return decodeURIComponent(tag);
1730 } catch (ex) {
1731 return unescape(tag);
1732 }
1733 }
1734 }
1735 }
1736 }
1737 return null;
1738 }
1739 },
1740 "link" : {
1741 virtual: true,
1742 datatype: "anyURI"
1743 },
1744 "text" : {
1745 virtual: true
1746 }
1747 },
validTagName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1748 validTagName: function(tag)
1749 {
1750 var returnTag = tag;
1751 if (tag.indexOf('?') != -1) {
1752 if (tag.indexOf('?') === 0) {
1753 return false;
1754 } else {
1755 returnTag = tag.substr(0, tag.indexOf('?'));
1756 }
1757 }
1758 if (tag.indexOf('#') != -1) {
1759 if (tag.indexOf('#') === 0) {
1760 return false;
1761 } else {
1762 returnTag = tag.substr(0, tag.indexOf('#'));
1763 }
1764 }
1765 if (tag.indexOf('.html') != -1) {
1766 if (tag.indexOf('.html') == tag.length - 5) {
1767 return false;
1768 }
1769 }
1770 return returnTag;
1771 },
validate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1772 validate: function(node) {
1773 var tag = Microformats.parser.getMicroformatProperty(node, "tag", "tag");
1774 if (!tag) {
1775 if (node.href) {
1776 var url_array = node.getAttribute("href").split("/");
1777 for(let i=url_array.length-1; i > 0; i--) {
1778 if (url_array[i] !== "") {
1779 throw("Invalid tag name (" + url_array[i] + ")");;
1780 }
1781 }
1782 } else {
1783 throw("No href specified on tag");
1784 }
1785 }
1786 return true;
1787 }
1788 };
1789
1790 Microformats.add("tag", tag_definition);