diff options
Diffstat (limited to 'toolkit/components/microformats/test/lib/parser.js')
-rw-r--r-- | toolkit/components/microformats/test/lib/parser.js | 1453 |
1 files changed, 1453 insertions, 0 deletions
diff --git a/toolkit/components/microformats/test/lib/parser.js b/toolkit/components/microformats/test/lib/parser.js new file mode 100644 index 000000000..062ec9f0e --- /dev/null +++ b/toolkit/components/microformats/test/lib/parser.js @@ -0,0 +1,1453 @@ +/*! + Parser + + Copyright (C) 2010 - 2015 Glenn Jones. All Rights Reserved. + MIT License: https://raw.github.com/glennjones/microformat-shiv/master/license.txt + Dependencies dates.js, domutils.js, html.js, isodate,js, text.js, utilities.js, url.js +*/ + + +var Modules = (function (modules) { + + + /** + * constructor + * + */ + modules.Parser = function () { + this.rootPrefix = 'h-'; + this.propertyPrefixes = ['p-', 'dt-', 'u-', 'e-']; + this.excludeTags = ['br', 'hr']; + }; + + + // create objects incase the v1 map modules don't load + modules.maps = (modules.maps)? modules.maps : {}; + modules.rels = (modules.rels)? modules.rels : {}; + + + modules.Parser.prototype = { + + init: function(){ + this.rootNode = null; + this.document = null; + this.options = { + 'baseUrl': '', + 'filters': [], + 'textFormat': 'whitespacetrimmed', + 'dateFormat': 'auto', // html5 for testing + 'overlappingVersions': false, + 'impliedPropertiesByVersion': true, + 'parseLatLonGeo': false + }; + this.rootID = 0; + this.errors = []; + this.noContentErr = 'No options.node or options.html was provided and no document object could be found.'; + }, + + + /** + * internal parse function + * + * @param {Object} options + * @return {Object} + */ + get: function(options) { + var out = this.formatEmpty(), + data = [], + rels; + + this.init(); + options = (options)? options : {}; + this.mergeOptions(options); + this.getDOMContext( options ); + + // if we do not have any context create error + if(!this.rootNode || !this.document){ + this.errors.push(this.noContentErr); + }else{ + + // only parse h-* microformats if we need to + // this is added to speed up parsing + if(this.hasMicroformats(this.rootNode, options)){ + this.prepareDOM( options ); + + if(this.options.filters.length > 0){ + // parse flat list of items + var newRootNode = this.findFilterNodes(this.rootNode, this.options.filters); + data = this.walkRoot(newRootNode); + }else{ + // parse whole document from root + data = this.walkRoot(this.rootNode); + } + + out.items = data; + // don't clear-up DOM if it was cloned + if(modules.domUtils.canCloneDocument(this.document) === false){ + this.clearUpDom(this.rootNode); + } + } + + // find any rels + if(this.findRels){ + rels = this.findRels(this.rootNode); + out.rels = rels.rels; + out['rel-urls'] = rels['rel-urls']; + } + + } + + if(this.errors.length > 0){ + return this.formatError(); + } + return out; + }, + + + /** + * parse to get parent microformat of passed node + * + * @param {DOM Node} node + * @param {Object} options + * @return {Object} + */ + getParent: function(node, options) { + this.init(); + options = (options)? options : {}; + + if(node){ + return this.getParentTreeWalk(node, options); + }else{ + this.errors.push(this.noContentErr); + return this.formatError(); + } + }, + + + /** + * get the count of microformats + * + * @param {DOM Node} rootNode + * @return {Int} + */ + count: function( options ) { + var out = {}, + items, + classItems, + x, + i; + + this.init(); + options = (options)? options : {}; + this.getDOMContext( options ); + + // if we do not have any context create error + if(!this.rootNode || !this.document){ + return {'errors': [this.noContentErr]}; + }else{ + + items = this.findRootNodes( this.rootNode, true ); + i = items.length; + while(i--) { + classItems = modules.domUtils.getAttributeList(items[i], 'class'); + x = classItems.length; + while(x--) { + // find v2 names + if(modules.utils.startWith( classItems[x], 'h-' )){ + this.appendCount(classItems[x], 1, out); + } + // find v1 names + for(var key in modules.maps) { + // dont double count if v1 and v2 roots are present + if(modules.maps[key].root === classItems[x] && classItems.indexOf(key) === -1) { + this.appendCount(key, 1, out); + } + } + } + } + var relCount = this.countRels( this.rootNode ); + if(relCount > 0){ + out.rels = relCount; + } + + return out; + } + }, + + + /** + * does a node have a class that marks it as a microformats root + * + * @param {DOM Node} node + * @param {Objecte} options + * @return {Boolean} + */ + isMicroformat: function( node, options ) { + var classes, + i; + + if(!node){ + return false; + } + + // if documemt gets topmost node + node = modules.domUtils.getTopMostNode( node ); + + // look for h-* microformats + classes = this.getUfClassNames(node); + if(options && options.filters && modules.utils.isArray(options.filters)){ + i = options.filters.length; + while(i--) { + if(classes.root.indexOf(options.filters[i]) > -1){ + return true; + } + } + return false; + }else{ + return (classes.root.length > 0); + } + }, + + + /** + * does a node or its children have microformats + * + * @param {DOM Node} node + * @param {Objecte} options + * @return {Boolean} + */ + hasMicroformats: function( node, options ) { + var items, + i; + + if(!node){ + return false; + } + + // if browser based documemt get topmost node + node = modules.domUtils.getTopMostNode( node ); + + // returns all microformat roots + items = this.findRootNodes( node, true ); + if(options && options.filters && modules.utils.isArray(options.filters)){ + i = items.length; + while(i--) { + if( this.isMicroformat( items[i], options ) ){ + return true; + } + } + return false; + }else{ + return (items.length > 0); + } + }, + + + /** + * add a new v1 mapping object to parser + * + * @param {Array} maps + */ + add: function( maps ){ + maps.forEach(function(map){ + if(map && map.root && map.name && map.properties){ + modules.maps[map.name] = JSON.parse(JSON.stringify(map)); + } + }); + }, + + + /** + * internal parse to get parent microformats by walking up the tree + * + * @param {DOM Node} node + * @param {Object} options + * @param {Int} recursive + * @return {Object} + */ + getParentTreeWalk: function (node, options, recursive) { + options = (options)? options : {}; + + // recursive calls + if (recursive === undefined) { + if (node.parentNode && node.nodeName !== 'HTML'){ + return this.getParentTreeWalk(node.parentNode, options, true); + }else{ + return this.formatEmpty(); + } + } + if (node !== null && node !== undefined && node.parentNode) { + if (this.isMicroformat( node, options )) { + // if we have a match return microformat + options.node = node; + return this.get( options ); + }else{ + return this.getParentTreeWalk(node.parentNode, options, true); + } + }else{ + return this.formatEmpty(); + } + }, + + + + /** + * configures what are the base DOM objects for parsing + * + * @param {Object} options + */ + getDOMContext: function( options ){ + var nodes = modules.domUtils.getDOMContext( options ); + this.rootNode = nodes.rootNode; + this.document = nodes.document; + }, + + + /** + * prepares DOM before the parse begins + * + * @param {Object} options + * @return {Boolean} + */ + prepareDOM: function( options ){ + var baseTag, + href; + + // use current document to define baseUrl, try/catch needed for IE10+ error + try { + if (!options.baseUrl && this.document && this.document.location) { + this.options.baseUrl = this.document.location.href; + } + } catch (e) { + // there is no alt action + } + + + // find base tag to set baseUrl + baseTag = modules.domUtils.querySelector(this.document,'base'); + if(baseTag) { + href = modules.domUtils.getAttribute(baseTag, 'href'); + if(href){ + this.options.baseUrl = href; + } + } + + // get path to rootNode + // then clone document + // then reset the rootNode to its cloned version in a new document + var path, + newDocument, + newRootNode; + + path = modules.domUtils.getNodePath(this.rootNode); + newDocument = modules.domUtils.cloneDocument(this.document); + newRootNode = modules.domUtils.getNodeByPath(newDocument, path); + + // check results as early IE fails + if(newDocument && newRootNode){ + this.document = newDocument; + this.rootNode = newRootNode; + } + + // add includes + if(this.addIncludes){ + this.addIncludes( this.document ); + } + + return (this.rootNode && this.document); + }, + + + /** + * returns an empty structure with errors + * + * @return {Object} + */ + formatError: function(){ + var out = this.formatEmpty(); + out.errors = this.errors; + return out; + }, + + + /** + * returns an empty structure + * + * @return {Object} + */ + formatEmpty: function(){ + return { + 'items': [], + 'rels': {}, + 'rel-urls': {} + }; + }, + + + // find microformats of a given type and return node structures + findFilterNodes: function(rootNode, filters) { + var newRootNode = modules.domUtils.createNode('div'), + items = this.findRootNodes(rootNode, true), + i = 0, + x = 0, + y = 0; + + if(items){ + i = items.length; + while(x < i) { + // add v1 names + y = filters.length; + while (y--) { + if(this.getMapping(filters[y])){ + var v1Name = this.getMapping(filters[y]).root; + filters.push(v1Name); + } + } + // append matching nodes into newRootNode + y = filters.length; + while (y--) { + if(modules.domUtils.hasAttributeValue(items[x], 'class', filters[y])){ + var clone = modules.domUtils.clone(items[x]); + modules.domUtils.appendChild(newRootNode, clone); + break; + } + } + x++; + } + } + + return newRootNode; + }, + + + /** + * appends data to output object for count + * + * @param {string} name + * @param {Int} count + * @param {Object} + */ + appendCount: function(name, count, out){ + if(out[name]){ + out[name] = out[name] + count; + }else{ + out[name] = count; + } + }, + + + /** + * is the microformats type in the filter list + * + * @param {Object} uf + * @param {Array} filters + * @return {Boolean} + */ + shouldInclude: function(uf, filters) { + var i; + + if(modules.utils.isArray(filters) && filters.length > 0) { + i = filters.length; + while(i--) { + if(uf.type[0] === filters[i]) { + return true; + } + } + return false; + } else { + return true; + } + }, + + + /** + * finds all microformat roots in a rootNode + * + * @param {DOM Node} rootNode + * @param {Boolean} includeRoot + * @return {Array} + */ + findRootNodes: function(rootNode, includeRoot) { + var arr = null, + out = [], + classList = [], + items, + x, + i, + y, + key; + + + // build an array of v1 root names + for(key in modules.maps) { + if (modules.maps.hasOwnProperty(key)) { + classList.push(modules.maps[key].root); + } + } + + // get all elements that have a class attribute + includeRoot = (includeRoot) ? includeRoot : false; + if(includeRoot && rootNode.parentNode) { + arr = modules.domUtils.getNodesByAttribute(rootNode.parentNode, 'class'); + } else { + arr = modules.domUtils.getNodesByAttribute(rootNode, 'class'); + } + + // loop elements that have a class attribute + x = 0; + i = arr.length; + while(x < i) { + + items = modules.domUtils.getAttributeList(arr[x], 'class'); + + // loop classes on an element + y = items.length; + while(y--) { + // match v1 root names + if(classList.indexOf(items[y]) > -1) { + out.push(arr[x]); + break; + } + + // match v2 root name prefix + if(modules.utils.startWith(items[y], 'h-')) { + out.push(arr[x]); + break; + } + } + + x++; + } + return out; + }, + + + /** + * starts the tree walk to find microformats + * + * @param {DOM Node} node + * @return {Array} + */ + walkRoot: function(node){ + var context = this, + children = [], + child, + classes, + items = [], + out = []; + + classes = this.getUfClassNames(node); + // if it is a root microformat node + if(classes && classes.root.length > 0){ + items = this.walkTree(node); + + if(items.length > 0){ + out = out.concat(items); + } + }else{ + // check if there are children and one of the children has a root microformat + children = modules.domUtils.getChildren( node ); + if(children && children.length > 0 && this.findRootNodes(node, true).length > -1){ + for (var i = 0; i < children.length; i++) { + child = children[i]; + items = context.walkRoot(child); + if(items.length > 0){ + out = out.concat(items); + } + } + } + } + return out; + }, + + + /** + * starts the tree walking for a single microformat + * + * @param {DOM Node} node + * @return {Array} + */ + walkTree: function(node) { + var classes, + out = [], + obj, + itemRootID; + + // loop roots found on one element + classes = this.getUfClassNames(node); + if(classes && classes.root.length && classes.root.length > 0){ + + this.rootID++; + itemRootID = this.rootID; + obj = this.createUfObject(classes.root, classes.typeVersion); + + this.walkChildren(node, obj, classes.root, itemRootID, classes); + if(this.impliedRules){ + this.impliedRules(node, obj, classes); + } + out.push( this.cleanUfObject(obj) ); + + + } + return out; + }, + + + /** + * finds child properties of microformat + * + * @param {DOM Node} node + * @param {Object} out + * @param {String} ufName + * @param {Int} rootID + * @param {Object} parentClasses + */ + walkChildren: function(node, out, ufName, rootID, parentClasses) { + var context = this, + children = [], + rootItem, + itemRootID, + value, + propertyName, + propertyVersion, + i, + x, + y, + z, + child; + + children = modules.domUtils.getChildren( node ); + + y = 0; + z = children.length; + while(y < z) { + child = children[y]; + + // get microformat classes for this single element + var classes = context.getUfClassNames(child, ufName); + + // a property which is a microformat + if(classes.root.length > 0 && classes.properties.length > 0 && !child.addedAsRoot) { + // create object with type, property and value + rootItem = context.createUfObject( + classes.root, + classes.typeVersion, + modules.text.parse(this.document, child, context.options.textFormat) + ); + + // add the microformat as an array of properties + propertyName = context.removePropPrefix(classes.properties[0][0]); + + // modifies value with "implied value rule" + if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){ + if(context.impliedValueRule){ + out = context.impliedValueRule(out, parentClasses.properties[0][0], classes.properties[0][0], value); + } + } + + if(out.properties[propertyName]) { + out.properties[propertyName].push(rootItem); + } else { + out.properties[propertyName] = [rootItem]; + } + + context.rootID++; + // used to stop duplication in heavily nested structures + child.addedAsRoot = true; + + + x = 0; + i = rootItem.type.length; + itemRootID = context.rootID; + while(x < i) { + context.walkChildren(child, rootItem, rootItem.type, itemRootID, classes); + x++; + } + if(this.impliedRules){ + context.impliedRules(child, rootItem, classes); + } + this.cleanUfObject(rootItem); + + } + + // a property which is NOT a microformat and has not been used for a given root element + if(classes.root.length === 0 && classes.properties.length > 0) { + + x = 0; + i = classes.properties.length; + while(x < i) { + + value = context.getValue(child, classes.properties[x][0], out); + propertyName = context.removePropPrefix(classes.properties[x][0]); + propertyVersion = classes.properties[x][1]; + + // modifies value with "implied value rule" + if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){ + if(context.impliedValueRule){ + out = context.impliedValueRule(out, parentClasses.properties[0][0], classes.properties[x][0], value); + } + } + + // if we have not added this value into a property with the same name already + if(!context.hasRootID(child, rootID, propertyName)) { + // check the root and property is the same version or if overlapping versions are allowed + if( context.isAllowedPropertyVersion( out.typeVersion, propertyVersion ) ){ + // add the property as an array of properties + if(out.properties[propertyName]) { + out.properties[propertyName].push(value); + } else { + out.properties[propertyName] = [value]; + } + // add rootid to node so we can track its use + context.appendRootID(child, rootID, propertyName); + } + } + + x++; + } + + context.walkChildren(child, out, ufName, rootID, classes); + } + + // if the node has no microformat classes, see if its children have + if(classes.root.length === 0 && classes.properties.length === 0) { + context.walkChildren(child, out, ufName, rootID, classes); + } + + // if the node is a child root add it to the children tree + if(classes.root.length > 0 && classes.properties.length === 0) { + + // create object with type, property and value + rootItem = context.createUfObject( + classes.root, + classes.typeVersion, + modules.text.parse(this.document, child, context.options.textFormat) + ); + + // add the microformat as an array of properties + if(!out.children){ + out.children = []; + } + + if(!context.hasRootID(child, rootID, 'child-root')) { + out.children.push( rootItem ); + context.appendRootID(child, rootID, 'child-root'); + context.rootID++; + } + + x = 0; + i = rootItem.type.length; + itemRootID = context.rootID; + while(x < i) { + context.walkChildren(child, rootItem, rootItem.type, itemRootID, classes); + x++; + } + if(this.impliedRules){ + context.impliedRules(child, rootItem, classes); + } + context.cleanUfObject( rootItem ); + + } + + + + y++; + } + + }, + + + + + /** + * gets the value of a property from a node + * + * @param {DOM Node} node + * @param {String} className + * @param {Object} uf + * @return {String || Object} + */ + getValue: function(node, className, uf) { + var value = ''; + + if(modules.utils.startWith(className, 'p-')) { + value = this.getPValue(node, true); + } + + if(modules.utils.startWith(className, 'e-')) { + value = this.getEValue(node); + } + + if(modules.utils.startWith(className, 'u-')) { + value = this.getUValue(node, true); + } + + if(modules.utils.startWith(className, 'dt-')) { + value = this.getDTValue(node, className, uf, true); + } + return value; + }, + + + /** + * gets the value of a node which contains a 'p-' property + * + * @param {DOM Node} node + * @param {Boolean} valueParse + * @return {String} + */ + getPValue: function(node, valueParse) { + var out = ''; + if(valueParse) { + out = this.getValueClass(node, 'p'); + } + + if(!out && valueParse) { + out = this.getValueTitle(node); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['data','input'], 'value'); + } + + if(node.name === 'br' || node.name === 'hr') { + out = ''; + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['img', 'area'], 'alt'); + } + + if(!out) { + out = modules.text.parse(this.document, node, this.options.textFormat); + } + + return(out) ? out : ''; + }, + + + /** + * gets the value of a node which contains the 'e-' property + * + * @param {DOM Node} node + * @return {Object} + */ + getEValue: function(node) { + + var out = {value: '', html: ''}; + + this.expandURLs(node, 'src', this.options.baseUrl); + this.expandURLs(node, 'href', this.options.baseUrl); + + out.value = modules.text.parse(this.document, node, this.options.textFormat); + out.html = modules.html.parse(node); + + return out; + }, + + + /** + * gets the value of a node which contains the 'u-' property + * + * @param {DOM Node} node + * @param {Boolean} valueParse + * @return {String} + */ + getUValue: function(node, valueParse) { + var out = ''; + if(valueParse) { + out = this.getValueClass(node, 'u'); + } + + if(!out && valueParse) { + out = this.getValueTitle(node); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['a', 'area'], 'href'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['img','audio','video','source'], 'src'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['object'], 'data'); + } + + // if we have no protocol separator, turn relative url to absolute url + if(out && out !== '' && out.indexOf('://') === -1) { + out = modules.url.resolve(out, this.options.baseUrl); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['data','input'], 'value'); + } + + if(!out) { + out = modules.text.parse(this.document, node, this.options.textFormat); + } + + return(out) ? out : ''; + }, + + + /** + * gets the value of a node which contains the 'dt-' property + * + * @param {DOM Node} node + * @param {String} className + * @param {Object} uf + * @param {Boolean} valueParse + * @return {String} + */ + getDTValue: function(node, className, uf, valueParse) { + var out = ''; + + if(valueParse) { + out = this.getValueClass(node, 'dt'); + } + + if(!out && valueParse) { + out = this.getValueTitle(node); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['time', 'ins', 'del'], 'datetime'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['data', 'input'], 'value'); + } + + if(!out) { + out = modules.text.parse(this.document, node, this.options.textFormat); + } + + if(out) { + if(modules.dates.isDuration(out)) { + // just duration + return out; + } else if(modules.dates.isTime(out)) { + // just time or time+timezone + if(uf) { + uf.times.push([className, modules.dates.parseAmPmTime(out, this.options.dateFormat)]); + } + return modules.dates.parseAmPmTime(out, this.options.dateFormat); + } else { + // returns a date - microformat profile + if(uf) { + uf.dates.push([className, new modules.ISODate(out).toString( this.options.dateFormat )]); + } + return new modules.ISODate(out).toString( this.options.dateFormat ); + } + } else { + return ''; + } + }, + + + /** + * appends a new rootid to a given node + * + * @param {DOM Node} node + * @param {String} id + * @param {String} propertyName + */ + appendRootID: function(node, id, propertyName) { + if(this.hasRootID(node, id, propertyName) === false){ + var rootids = []; + if(modules.domUtils.hasAttribute(node,'rootids')){ + rootids = modules.domUtils.getAttributeList(node,'rootids'); + } + rootids.push('id' + id + '-' + propertyName); + modules.domUtils.setAttribute(node, 'rootids', rootids.join(' ')); + } + }, + + + /** + * does a given node already have a rootid + * + * @param {DOM Node} node + * @param {String} id + * @param {String} propertyName + * @return {Boolean} + */ + hasRootID: function(node, id, propertyName) { + var rootids = []; + if(!modules.domUtils.hasAttribute(node,'rootids')){ + return false; + } else { + rootids = modules.domUtils.getAttributeList(node, 'rootids'); + return (rootids.indexOf('id' + id + '-' + propertyName) > -1); + } + }, + + + + /** + * gets the text of any child nodes with a class value + * + * @param {DOM Node} node + * @param {String} propertyName + * @return {String || null} + */ + getValueClass: function(node, propertyType) { + var context = this, + children = [], + out = [], + child, + x, + i; + + children = modules.domUtils.getChildren( node ); + + x = 0; + i = children.length; + while(x < i) { + child = children[x]; + var value = null; + if(modules.domUtils.hasAttributeValue(child, 'class', 'value')) { + switch(propertyType) { + case 'p': + value = context.getPValue(child, false); + break; + case 'u': + value = context.getUValue(child, false); + break; + case 'dt': + value = context.getDTValue(child, '', null, false); + break; + } + if(value) { + out.push(modules.utils.trim(value)); + } + } + x++; + } + if(out.length > 0) { + if(propertyType === 'p') { + return modules.text.parseText( this.document, out.join(' '), this.options.textFormat); + } + if(propertyType === 'u') { + return out.join(''); + } + if(propertyType === 'dt') { + return modules.dates.concatFragments(out,this.options.dateFormat).toString(this.options.dateFormat); + } + } else { + return null; + } + }, + + + /** + * returns a single string of the 'title' attr from all + * the child nodes with the class 'value-title' + * + * @param {DOM Node} node + * @return {String} + */ + getValueTitle: function(node) { + var out = [], + items, + i, + x; + + items = modules.domUtils.getNodesByAttributeValue(node, 'class', 'value-title'); + x = 0; + i = items.length; + while(x < i) { + if(modules.domUtils.hasAttribute(items[x], 'title')) { + out.push(modules.domUtils.getAttribute(items[x], 'title')); + } + x++; + } + return out.join(''); + }, + + + /** + * finds out whether a node has h-* class v1 and v2 + * + * @param {DOM Node} node + * @return {Boolean} + */ + hasHClass: function(node){ + var classes = this.getUfClassNames(node); + if(classes.root && classes.root.length > 0){ + return true; + }else{ + return false; + } + }, + + + /** + * get both the root and property class names from a node + * + * @param {DOM Node} node + * @param {Array} ufNameArr + * @return {Object} + */ + getUfClassNames: function(node, ufNameArr) { + var context = this, + out = { + 'root': [], + 'properties': [] + }, + classNames, + key, + items, + item, + i, + x, + z, + y, + map, + prop, + propName, + v2Name, + impiedRel, + ufName; + + // don't get classes from excluded list of tags + if(modules.domUtils.hasTagName(node, this.excludeTags) === false){ + + // find classes for node + classNames = modules.domUtils.getAttribute(node, 'class'); + if(classNames) { + items = classNames.split(' '); + x = 0; + i = items.length; + while(x < i) { + + item = modules.utils.trim(items[x]); + + // test for root prefix - v2 + if(modules.utils.startWith(item, context.rootPrefix)) { + if(out.root.indexOf(item) === -1){ + out.root.push(item); + } + out.typeVersion = 'v2'; + } + + // test for property prefix - v2 + z = context.propertyPrefixes.length; + while(z--) { + if(modules.utils.startWith(item, context.propertyPrefixes[z])) { + out.properties.push([item,'v2']); + } + } + + // test for mapped root classnames v1 + for(key in modules.maps) { + if(modules.maps.hasOwnProperty(key)) { + // only add a root once + if(modules.maps[key].root === item && out.root.indexOf(key) === -1) { + // if root map has subTree set to true + // test to see if we should create a property or root + if(modules.maps[key].subTree) { + out.properties.push(['p-' + modules.maps[key].root, 'v1']); + } else { + out.root.push(key); + if(!out.typeVersion){ + out.typeVersion = 'v1'; + } + } + } + } + } + + + // test for mapped property classnames v1 + if(ufNameArr){ + for (var a = 0; a < ufNameArr.length; a++) { + ufName = ufNameArr[a]; + // get mapped property v1 microformat + map = context.getMapping(ufName); + if(map) { + for(key in map.properties) { + if (map.properties.hasOwnProperty(key)) { + + prop = map.properties[key]; + propName = (prop.map) ? prop.map : 'p-' + key; + + if(key === item) { + if(prop.uf) { + // loop all the classList make sure + // 1. this property is a root + // 2. that there is not already an equivalent v2 property i.e. url and u-url on the same element + y = 0; + while(y < i) { + v2Name = context.getV2RootName(items[y]); + // add new root + if(prop.uf.indexOf(v2Name) > -1 && out.root.indexOf(v2Name) === -1) { + out.root.push(v2Name); + out.typeVersion = 'v1'; + } + y++; + } + //only add property once + if(out.properties.indexOf(propName) === -1) { + out.properties.push([propName,'v1']); + } + } else { + if(out.properties.indexOf(propName) === -1) { + out.properties.push([propName,'v1']); + } + } + } + } + + } + } + } + + } + + x++; + + } + } + } + + + // finds any alt rel=* mappings for a given node/microformat + if(ufNameArr && this.findRelImpied){ + for (var b = 0; b < ufNameArr.length; b++) { + ufName = ufNameArr[b]; + impiedRel = this.findRelImpied(node, ufName); + if(impiedRel && out.properties.indexOf(impiedRel) === -1) { + out.properties.push([impiedRel, 'v1']); + } + } + } + + + //if(out.root.length === 1 && out.properties.length === 1) { + // if(out.root[0].replace('h-','') === this.removePropPrefix(out.properties[0][0])) { + // out.typeVersion = 'v2'; + // } + //} + + return out; + }, + + + /** + * given a v1 or v2 root name, return mapping object + * + * @param {String} name + * @return {Object || null} + */ + getMapping: function(name) { + var key; + for(key in modules.maps) { + if(modules.maps[key].root === name || key === name) { + return modules.maps[key]; + } + } + return null; + }, + + + /** + * given a v1 root name returns a v2 root name i.e. vcard >>> h-card + * + * @param {String} name + * @return {String || null} + */ + getV2RootName: function(name) { + var key; + for(key in modules.maps) { + if(modules.maps[key].root === name) { + return key; + } + } + return null; + }, + + + /** + * whether a property is the right microformats version for its root type + * + * @param {String} typeVersion + * @param {String} propertyVersion + * @return {Boolean} + */ + isAllowedPropertyVersion: function(typeVersion, propertyVersion){ + if(this.options.overlappingVersions === true){ + return true; + }else{ + return (typeVersion === propertyVersion); + } + }, + + + /** + * creates a blank microformats object + * + * @param {String} name + * @param {String} value + * @return {Object} + */ + createUfObject: function(names, typeVersion, value) { + var out = {}; + + // is more than just whitespace + if(value && modules.utils.isOnlyWhiteSpace(value) === false) { + out.value = value; + } + // add type i.e. ["h-card", "h-org"] + if(modules.utils.isArray(names)) { + out.type = names; + } else { + out.type = [names]; + } + out.properties = {}; + // metadata properties for parsing + out.typeVersion = typeVersion; + out.times = []; + out.dates = []; + out.altValue = null; + + return out; + }, + + + /** + * removes unwanted microformats property before output + * + * @param {Object} microformat + */ + cleanUfObject: function( microformat ) { + delete microformat.times; + delete microformat.dates; + delete microformat.typeVersion; + delete microformat.altValue; + return microformat; + }, + + + + /** + * removes microformat property prefixes from text + * + * @param {String} text + * @return {String} + */ + removePropPrefix: function(text) { + var i; + + i = this.propertyPrefixes.length; + while(i--) { + var prefix = this.propertyPrefixes[i]; + if(modules.utils.startWith(text, prefix)) { + text = text.substr(prefix.length); + } + } + return text; + }, + + + /** + * expands all relative URLs to absolute ones where it can + * + * @param {DOM Node} node + * @param {String} attrName + * @param {String} baseUrl + */ + expandURLs: function(node, attrName, baseUrl){ + var i, + nodes, + attr; + + nodes = modules.domUtils.getNodesByAttribute(node, attrName); + i = nodes.length; + while (i--) { + try{ + // the url parser can blow up if the format is not right + attr = modules.domUtils.getAttribute(nodes[i], attrName); + if(attr && attr !== '' && baseUrl !== '' && attr.indexOf('://') === -1) { + //attr = urlParser.resolve(baseUrl, attr); + attr = modules.url.resolve(attr, baseUrl); + modules.domUtils.setAttribute(nodes[i], attrName, attr); + } + }catch(err){ + // do nothing - convert only the urls we can, leave the rest as they are + } + } + }, + + + + /** + * merges passed and default options -single level clone of properties + * + * @param {Object} options + */ + mergeOptions: function(options) { + var key; + for(key in options) { + if(options.hasOwnProperty(key)) { + this.options[key] = options[key]; + } + } + }, + + + /** + * removes all rootid attributes + * + * @param {DOM Node} rootNode + */ + removeRootIds: function(rootNode){ + var arr, + i; + + arr = modules.domUtils.getNodesByAttribute(rootNode, 'rootids'); + i = arr.length; + while(i--) { + modules.domUtils.removeAttribute(arr[i],'rootids'); + } + }, + + + /** + * removes all changes made to the DOM + * + * @param {DOM Node} rootNode + */ + clearUpDom: function(rootNode){ + if(this.removeIncludes){ + this.removeIncludes(rootNode); + } + this.removeRootIds(rootNode); + } + + + }; + + + modules.Parser.prototype.constructor = modules.Parser; + + return modules; + +} (Modules || {})); + + + |