Permalink
Browse files

Refactored a bit, added validation info to the results of

`parseElement()`

Also, added test for missing property
  • Loading branch information...
1 parent e0b7335 commit 3bb214ff4073d45e9b4a7dde6e8b671a7dec1981 @KrofDrakula committed Feb 5, 2011
Showing with 150 additions and 59 deletions.
  1. +117 −49 jquery.microdata.js
  2. +6 −1 test.html
  3. +27 −9 test.js
View
166 jquery.microdata.js
@@ -1,3 +1,4 @@
+/*globals jQuery */
(function($) {
var items = [], widget,
@@ -22,23 +23,23 @@
validators = {
text: function(value, el) { return $.trim(value).length > 0; },
- url: function(value, el) { return /^https?:\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?$/.test(value); },
+ url: function(value, el) { return (/^https?:\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?$/).test(value); },
- int: function(value, el) { return /^\d+$/.test(value); },
+ int: function(value, el) { return (/^\d+$/).test(value); },
- float: function(value, el) { return /^\d+([.,]\d*)?$/.test(value); },
+ float: function(value, el) { return (/^\d+([.,]\d*)?$/).test(value); },
// TODO: need a proper datetime validator
- datetime: function(value, el) { return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?$/.test(value); },
+ datetime: function(value, el) { return (/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?$/).test(value); },
// TODO: check the duration per http://en.wikipedia.org/wiki/ISO_8601#Durations
- duration: function(value, el) { return /^P(([0-9.,]+[YMD])*(T[0-9.,]+[HMS])*|[0-9.,]W)$/.test(value); },
+ duration: function(value, el) { return (/^P(([0-9.,]+[YMD])*(T[0-9.,]+[HMS])*|[0-9.,]W)$/).test(value); },
// TODO: complex types require other nested entities or special treatment
complex: function(value, el) { return true; },
// TODO: check three-letter ISO code for currency: http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes.htm
- currency: function(value, el) { return /^[a-zA-Z]{3}$/.test(value); },
+ currency: function(value, el) { return (/^[a-zA-Z]{3}$/).test(value); },
any: function(value, el) { return true; }
},
@@ -115,6 +116,92 @@
]
};
+
+ /**
+ * Takes the result of parseElement and attaches the "valid" and "missing" properties,
+ * then returns the augmented object
+ */
+ var validateData = function(mdata) {
+ var i, rules, required, rule;
+
+ if(!validationRules.hasOwnProperty(mdata.type)) {
+ for(i = 0; i < mdata.properties.length; i++) {
+ mdata.properties[i].valid = true;
+ }
+ mdata.valid = true;
+ mdata.missing = [];
+ return mdata;
+ }
+
+ // check type validation
+ mdata.missing = [];
+ mdata.valid = true;
+
+ rules = validationRules[mdata.type];
+ required = $.grep(rules, function(item) { return item.required; });
+
+ for(i = 0; i < mdata.properties.length; i++) {
+ rule = $.grep(rules, function(item) { return item.name === mdata.properties[i].name.toLowerCase(); });
+
+ // pop the field from the required list
+ required = $.grep(required, function(item) { return item.name === mdata.properties[i].name.toLowerCase(); }, true);
+
+ if(rule.length > 0 && !rule[0].validator(mdata.properties[i].value)) {
+ mdata.properties[i].valid = false;
+ mdata.valid = false;
+ } else {
+ mdata.properties[i].valid = true;
+ }
+ }
+
+ // any required properties not defined are appended to the list
+ if(required.length > 0) {
+ for(i = 0; i < required.length; i++) {
+ mdata.missing.push(required[i].name);
+ }
+ mdata.valid = false;
+ }
+
+ return mdata;
+ };
+
+ /**
+ * Utility functions that extracts the relevant data
+ */
+ var parseElement = function(el) {
+ if(!el.jquery) { el = $(el); }
+ // if the element in question isn't an itemscope, return null
+ if(!el.hasAttr('itemscope')) { return null; }
+
+ var propElements = el.find('[itemprop]'), props = [], i;
+ propElements.each(function() {
+ var $p = $(this), propname = $p.attr('itemprop').toLowerCase().split(' '), v;
+
+ for (i = 0; i < propname.length; i++) {
+ v = $p.text();
@pomeh
pomeh added a line comment Feb 28, 2011

I'm maybe wrong but it seems like $p doesn't change during the for loop. Why don't you cache the result of $p.text() outside the loop ? You could also use that cache variable in the $p.is('time') code block bellow to avoid one extra call.
Also, why don't you cache all of the $p.is('whatever') results ? They won't change either.

@KrofDrakula
Owner
KrofDrakula added a line comment Feb 28, 2011

True, I was rushing a bit with the code to make it as functional as possible in as little time as I could, haven't really done much in terms of optimization. You're welcome to refactor and post a pull request and I'll merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ if ($p.is('a,area,link')) {
+ v = $p.attr('href');
+ } else if ($p.is('audio,embed,iframe,img,source,video')) {
+ v = $p.attr('src');
+ } else if ($p.is('object')) {
+ v = $p.attr('data');
+ } else if ($p.is('time')) {
+ v = $p.attr('datetime') || $p.text();
+ }
+
+ props.push({
+ name : propname[i],
+ value : v
+ });
+ }
+ });
+
+ return validateData({ type: el.attr('itemtype') || null, properties: props });
+ };
+
+
+
/**
* Updates the list of microdata elements on the page
*/
@@ -137,34 +224,43 @@
var addObject = function(element, mdata) {
var type = mdata.type,
t = $('<li title="' + type + '">' + (validators.url(type)? '<a href="' + type + '">' + type.replace(/^.*\//, '') + '</a>': "[no vocabulary]") + '</li>').appendTo(widget),
- u = $('<ul/>').appendTo(t), rules = [], required = [], rule, prop, validationExists = false;
+ u = $('<ul/>').appendTo(t),
+ rules = [],
+ required = [],
+ rule,
+ prop,
+ validationExists = false,
+ i;
+
+ mdata = (typeof mdata.valid === 'undefined')? mdata = validateData(mdata): mdata;
if(mdata.properties.length > 0) {
- if(validationRules.hasOwnProperty(type)) {
- rules = validationRules[type];
- required = $.grep(rules, function(item) { return item.required; });
- validationExists = true;
- }
- for(var i = 0; i < mdata.properties.length; i++) {
+
+ for(i = 0; i < mdata.properties.length; i++) {
prop = $('<li>' + mdata.properties[i].name + ' = ' + mdata.properties[i].value + '</li>').appendTo(u);
+ if(!mdata.properties[i].valid) {
+ prop.addClass('invalid');
+ }
+ /*
// if validation is present, validate the properties
if(validationExists) {
- rule = $.grep(rules, function(item) { return item.name == mdata.properties[i].name.toLowerCase(); });
+ rule = $.grep(rules, function(item) { return item.name === mdata.properties[i].name.toLowerCase(); });
// pop the field from the required list
- required = $.grep(required, function(item) { return item.name == mdata.properties[i].name.toLowerCase(); }, true);
+ required = $.grep(required, function(item) { return item.name === mdata.properties[i].name.toLowerCase(); }, true);
if(rule.length > 0 && !rule[0].validator(mdata.properties[i].value)) {
prop.addClass("invalid");
}
}
+ */
}
// any required properties not defined are appended to the list
- if(required.length > 0) {
- for(var i = 0; i < required.length; i++) {
- $('<li class="invalid">missing property: ' + required[i].name + '</li>').appendTo(u);
+ if(mdata.missing > 0) {
+ for(i = 0; i < mdata.missing.length; i++) {
+ $('<li class="invalid">missing property: ' + mdata.missing[i] + '</li>').appendTo(u);
}
}
} else {
@@ -187,7 +283,7 @@
clearObjects();
refreshList();
- if(items.length == 0) {
+ if(items.length === 0) {
$('<li class="invalid">No microdata objects detected!</li>').appendTo(widget);
return;
}
@@ -204,35 +300,7 @@
updateList();
};
- var parseElement = function(el) {
- if(!el.jquery) el = $(el);
- // if the element in question isn't an itemscope, return null
- if(!el.hasAttr('itemscope')) return null;
-
- var propElements = el.find('[itemprop]'), props = [];
- propElements.each(function() {
- var $p = $(this), propname = $p.attr('itemprop').toLowerCase().split(' ');
-
- for (var i = 0; i < propname.length; i++) {
- var v = $p.text();
-
- if ($p.is('a,area,link'))
- v = $p.attr('href');
- else if ($p.is('audio,embed,iframe,img,source,video'))
- v = $p.attr('src');
- else if ($p.is('object'))
- v = $p.attr('data');
- else if ($p.is('time')) v = $p.attr('datetime') || $p.text();
-
- props.push({
- name : propname[i],
- value : v
- });
- }
- });
-
- return { type: el.attr('itemtype') || null, properties: props };
- };
+
// expose functions for use by outside scripts via plugin
@@ -242,7 +310,7 @@
parseElement: parseElement,
defaults: {
scope: 'body'
- }
+ }
};
$.fn.hasAttr = function(name) {
View
7 test.html
@@ -8,7 +8,7 @@
<script src="jquery.microdata.js"></script>
<script src="test.js"></script>
</head>
- <body>
+ <body style="padding-bottom:200px">
<h1 id="qunit-header">QUnit example</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
@@ -37,6 +37,11 @@ <h2 id="qunit-userAgent"></h2>
<li itemscope id="multi-prop-single-value" itemtype="http://example.org/Multiprop">
<p itemprop="admin user">true</p>
</li>
+
+ <!-- single item with one required and one missing property per the vocabulary rules -->
+ <li itemscope id="single-item-missing-property" itemtype="http://data-vocabulary.org/Event">
+ <p itemprop="summary">This is the event summary</p>
+ </li>
</ul>
</div>
</body>
View
36 test.js
@@ -5,8 +5,9 @@ $(function() {
same(res, {
type: null,
properties: [
- { name: "description", value: "This is a description" }
- ]
+ { name: "description", value: "This is a description", valid: true }
+ ],
+ valid: true, missing: []
}, 'Should return null type, one property');
});
@@ -15,7 +16,8 @@ $(function() {
same($.microdata.parseElement($('#vocab-no-properties')), {
type: "http://example.org/Type",
- properties: []
+ properties: [],
+ valid: true, missing: []
}, 'Should have type set to example.org');
});
@@ -25,9 +27,10 @@ $(function() {
same($.microdata.parseElement($('#multi-value-single-prop')), {
type: "http://example.org/Multivalue",
properties: [
- { name: "attending", value: "Peter Bishop" },
- { name: "attending", value: "Walter Bishop" }
- ]
+ { name: "attending", value: "Peter Bishop", valid: true },
+ { name: "attending", value: "Walter Bishop", valid: true }
+ ],
+ valid: true, missing: []
}, 'Should return an array of values');
});
@@ -36,9 +39,24 @@ $(function() {
same($.microdata.parseElement($('#multi-prop-single-value')), {
type: "http://example.org/Multiprop",
properties: [
- { name: "admin", value: "true" },
- { name: "user", value: "true" }
- ]
+ { name: "admin", value: "true", valid: true },
+ { name: "user", value: "true", valid: true }
+ ],
+ valid: true, missing: []
}, 'Should return multiple properties with same value');
});
+
+
+ test('Single item with 1 missing property per vocabulary rules', function() {
+
+ same($.microdata.parseElement($('#single-item-missing-property')), {
+ type: "http://data-vocabulary.org/Event",
+ properties: [
+ { name: "summary", value: "This is the event summary", valid: true }
+ ],
+ missing: [ "startdate" ],
+ valid: false
+ }, 'Should return 1 good property and one missing');;
+
+ });
});

0 comments on commit 3bb214f

Please sign in to comment.