diff --git a/administrator/language/en-GB/en-GB.plg_system_microdata.ini b/administrator/language/en-GB/en-GB.plg_system_microdata.ini
new file mode 100644
index 0000000000000..4afb586aaf1c0
--- /dev/null
+++ b/administrator/language/en-GB/en-GB.plg_system_microdata.ini
@@ -0,0 +1,9 @@
+; Joomla! Project
+; Copyright (C) 2005 - 2014 Open Source Matters. All rights reserved.
+; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
+; Note : All ini files need to be saved as UTF-8 - No BOM
+
+PLG_SYSTEM_MICRODATA="System - Microdata"
+PLG_SYSTEM_MICRODATA_FIELD_SUFFIX_DESCRIPTION="The suffix to search for when parsing the data-* HTML5 attribute"
+PLG_SYSTEM_MICRODATA_FIELD_SUFFIX_LABEL="The data-* suffix"
+PLG_SYSTEM_MICRODATA_XML_DESCRIPTION="System Plugin for parsing the HTML markup and convert the 'data-sd' HTML5 attributes in Microdata semantics."
\ No newline at end of file
diff --git a/administrator/language/en-GB/en-GB.plg_system_microdata.sys.ini b/administrator/language/en-GB/en-GB.plg_system_microdata.sys.ini
new file mode 100644
index 0000000000000..a12b5ca36163d
--- /dev/null
+++ b/administrator/language/en-GB/en-GB.plg_system_microdata.sys.ini
@@ -0,0 +1,7 @@
+; Joomla! Project
+; Copyright (C) 2005 - 2014 Open Source Matters. All rights reserved.
+; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
+; Note : All ini files need to be saved as UTF-8 - No BOM
+
+PLG_SYSTEM_MICRODATA="System - Microdata"
+PLG_SYSTEM_MICRODATA_XML_DESCRIPTION="System Plugin for parsing the HTML markup and convert the 'data-sd' HTML5 attributes in Microdata semantics."
\ No newline at end of file
diff --git a/installation/sql/mysql/joomla.sql b/installation/sql/mysql/joomla.sql
index 0ad451a575d3a..f6d7a12ff7835 100644
--- a/installation/sql/mysql/joomla.sql
+++ b/installation/sql/mysql/joomla.sql
@@ -610,6 +610,7 @@ INSERT INTO `#__extensions` (`extension_id`, `name`, `type`, `element`, `folder`
(449, 'plg_authentication_cookie', 'plugin', 'cookie', 'authentication', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(450, 'plg_twofactorauth_yubikey', 'plugin', 'yubikey', 'twofactorauth', 0, 0, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(451, 'plg_search_tags', 'plugin', 'tags', 'search', 0, 1, 1, 0, '', '{"search_limit":"50","show_tagged_items":"1"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
+(452, 'plg_search_microdata', 'plugin', 'microdata', 'system', 0, 1, 1, 0, '', '{"suffix":"sd"}', '', '', 0, '0000-00-00 00:00:00', 100, 0),
(503, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(504, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(506, 'protostar', 'template', 'protostar', '', 0, 1, 1, 0, '', '{"templateColor":"","logoFile":"","googleFont":"1","googleFontName":"Open+Sans","fluidContainer":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
diff --git a/installation/sql/postgresql/joomla.sql b/installation/sql/postgresql/joomla.sql
index 011b918313835..30fc480647e2e 100644
--- a/installation/sql/postgresql/joomla.sql
+++ b/installation/sql/postgresql/joomla.sql
@@ -611,6 +611,7 @@ INSERT INTO "#__extensions" ("extension_id", "name", "type", "element", "folder"
(449, 'plg_authentication_cookie', 'plugin', 'cookie', 'authentication', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(450, 'plg_twofactorauth_yubikey', 'plugin', 'yubikey', 'twofactorauth', 0, 0, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(451, 'plg_search_tags', 'plugin', 'tags', 'search', 0, 1, 1, 0, '', '{"search_limit":"50","show_tagged_items":"1"}', '', '', 0, '1970-01-01 00:00:00', 0, 0);
+(452, 'plg_search_microdata', 'plugin', 'microdata', 'system', 0, 1, 1, 0, '', '{"suffix":"sd"}', '', '', 0, '0000-00-00 00:00:00', 100, 0);
-- Templates
INSERT INTO "#__extensions" ("extension_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "manifest_cache", "params", "custom_data", "system_data", "checked_out", "checked_out_time", "ordering", "state") VALUES
diff --git a/installation/sql/sqlazure/joomla.sql b/installation/sql/sqlazure/joomla.sql
index aae100f979ab0..f12b943c6348b 100644
--- a/installation/sql/sqlazure/joomla.sql
+++ b/installation/sql/sqlazure/joomla.sql
@@ -1012,7 +1012,8 @@ UNION ALL
SELECT 450, 'plg_twofactorauth_yubikey', 'plugin', 'yubikey', 'twofactorauth', 0, 0, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0
UNION ALL
SELECT 451, 'plg_search_tags', 'plugin', 'tags', 'search', 0, 1, 1, 0, '', '{"search_limit":"50","show_tagged_items":"1"}', '', '', 0, '1900-01-01 00:00:00', 0, 0;
-
+UNION ALL
+SELECT 452, 'plg_search_microdata', 'plugin', 'microdata', 'system', 0, 1, 1, 0, '', '{"suffix":"sd"}', '', '', 0, '0000-00-00 00:00:00', 100, 0;
INSERT [#__extensions] ([extension_id], [name], [type], [element], [folder], [client_id], [enabled], [access], [protected], [manifest_cache], [params], [custom_data], [system_data], [checked_out], [checked_out_time], [ordering], [state])
SELECT 503, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '1900-01-01 00:00:00', 0, 0
diff --git a/libraries/joomla/microdata/parser.php b/libraries/joomla/microdata/parser.php
new file mode 100644
index 0000000000000..6730a4512f3fa
--- /dev/null
+++ b/libraries/joomla/microdata/parser.php
@@ -0,0 +1,437 @@
+suffix($suffix);
+ }
+
+ $this->handler = new JMicrodata;
+ }
+
+ /**
+ * Return the $handler, which is an instance of JMicrodata
+ *
+ * @return JMicrodata
+ *
+ * @since 3.3
+ */
+ public function getHandler()
+ {
+ return $this->handler;
+ }
+
+ /**
+ * Setup the $suffix to search for when parsing the data-* HTML5 attribute
+ *
+ * @param mixed $suffix The suffix
+ *
+ * @return JMicrodataParser
+ *
+ * @since 3.3
+ */
+ public function suffix($suffix)
+ {
+ if (is_array($suffix))
+ {
+ while ($string = array_pop($suffix))
+ {
+ $this->addSuffix($string);
+ }
+
+ return $this;
+ }
+
+ $this->addSuffix($suffix);
+
+ return $this;
+ }
+
+ /**
+ * Add a new $suffix to search for when parsing the data-* HTML5 attribute
+ *
+ * @param string $string The suffix
+ *
+ * @return void
+ *
+ * @since 3.3
+ */
+ protected function addSuffix($string)
+ {
+ $string = trim(strtolower((string) $string));
+
+ // Avoid adding a duplicate suffix, also the suffix must be at least one character long
+ if (array_search($string, $this->suffix) || empty($string))
+ {
+ return;
+ }
+
+ // Add the new suffix
+ array_push($this->suffix, $string);
+ }
+
+ /**
+ * Remove a $suffix entry
+ *
+ * @param string $string The suffix
+ *
+ * @return JMicrodataParser
+ *
+ * @since 3.3
+ */
+ public function removeSuffix($string)
+ {
+ $string = strtolower((string) $string);
+
+ // Search and remove the suffix
+ unset(
+ $this->suffix[array_search($string, $this->suffix)]
+ );
+
+ return $this;
+ }
+
+ /**
+ * Return the current $suffix
+ *
+ * @return string
+ *
+ * @since 3.3
+ */
+ public function getSuffix()
+ {
+ return $this->suffix;
+ }
+
+ /**
+ * Parse the unit param that will be used to setup the JMicrodata class,
+ * e.g. giving the following: $string = 'Type.property.EType';
+ * will return an array:
+ * array(
+ * 'type' => 'Type,
+ * 'property' => 'property'
+ * 'expectedType => 'EType'
+ * );
+ *
+ * @param string $string The string to parse
+ *
+ * @return array
+ */
+ protected static function parseParam($string)
+ {
+ // The default array
+ $params = array(
+ 'type' => null,
+ 'property' => null,
+ 'expectedType' => null
+ );
+
+ // Sanitize the $string and parse
+ $string = explode('.', trim((string) $string));
+
+ // If no matches found return the default array
+ if (empty($string[0]))
+ {
+ return $params;
+ }
+
+ // If the first letter is uppercase, then the param string could be 'Type.property.EType', otherwise it should be the 'property.EType'
+ if (ctype_upper($string[0]{0}))
+ {
+ $params['type'] = $string[0];
+
+ // If the first letter is lowercase, then it should be the property, otherwise return
+ if (count($string) > 1 && !empty($string[1]) && ctype_lower($string[1]{0}))
+ {
+ $params['property'] = $string[1];
+
+ // If the first letter is uppercase, then it should be expected Type, otherwise return
+ if (count($string) > 2 && !empty($string[2]) && ctype_upper($string[2]{0}))
+ {
+ $params['expectedType'] = $string[2];
+ }
+ }
+ }
+ else
+ {
+ $params['property'] = $string[0];
+
+ // If the first letter is uppercase, then it should be the expectedType
+ if (count($string) > 1 && !empty($string[1]) && ctype_upper($string[1]{0}))
+ {
+ $params['expectedType'] = $string[1];
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Parse the params that will be used to setup the JMicrodata class,
+ * e.g giving the following: $string ='Type Type.property.EType ... FType.fProperty gProperty.EType sProperty';
+ * will return an array:
+ * array(
+ * 'setType' => 'Type',
+ * 'fallbacks' => array(
+ * 'specialized' => array(
+ * 'Type' => array('property' => 'EType'),
+ * 'FType' => array('fproperty' => null)
+ * ...
+ * ),
+ * 'global' => array(
+ * ...
+ * 'gProperty' => 'EType',
+ * 'sProperty' => null
+ * )
+ * )
+ * );
+ *
+ * @param string $string The string to parse
+ *
+ * @return array
+ */
+ protected static function parseParams($string)
+ {
+ // The default array
+ $params = array(
+ 'setType' => null,
+ 'fallbacks' => array(
+ 'specialized' => array(),
+ 'global' => array()
+ )
+ );
+
+ // Sanitize the $string, remove single and multiple whitespaces
+ $string = trim(preg_replace('/\s+/', ' ', (string) $string));
+
+ // Break the strings in small param chunks
+ $string = explode(' ', $string);
+
+ // Parse the small param chunks
+ foreach ($string as $match)
+ {
+ $tmp = self::parseParam($match);
+ $type = $tmp['type'];
+ $property = $tmp['property'];
+ $expectedType = $tmp['expectedType'];
+
+ // If a 'type' is available and there is no 'property', then it should be a 'setType'
+ if ($type && !$property && !$params['setType'])
+ {
+ $params['setType'] = $type;
+ }
+
+ // If a 'property' is available and there is no 'type', then it should be a 'global' fallback
+ if (!$type && $property)
+ {
+ $params['fallbacks']['global'][$property] = $expectedType;
+ }
+
+ // If both 'type' and 'property' is available, then it should be a 'specialized' fallback
+ if ($type && $property && !array_key_exists($type, $params['fallbacks']['specialized']))
+ {
+ $params['fallbacks']['specialized'][$type] = array($property => $expectedType);
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Generate the Microdata semantics
+ *
+ * @param array $params The params used to setup the JMicrodata library
+ *
+ * @return string
+ */
+ protected function display($params)
+ {
+ $html = '';
+ $setType = $params['setType'];
+
+ // Specialized fallbacks
+ $sFallbacks = $params['fallbacks']['specialized'];
+
+ // Global fallbacks
+ $gFallbacks = $params['fallbacks']['global'];
+
+ // Set the current Type if available
+ if ($setType)
+ {
+ $this->handler->setType($setType);
+ }
+
+ // If no properties available and there is a 'setType', return and display the scope
+ if ($setType && !$sFallbacks && !$gFallbacks)
+ {
+ return $this->handler->displayScope();
+ }
+
+ // Get the current Type
+ $currentType = $this->handler->getType();
+
+ // Check if there is an available 'specialized' fallback property for the current Type
+ if ($sFallbacks && array_key_exists($currentType, $sFallbacks))
+ {
+ $property = key($sFallbacks[$currentType]);
+ $expectedType = $sFallbacks[$currentType][$property];
+
+ $html .= $this->handler->property($property)->display('inline');
+
+ // Check if an expected Type is available and it is valid
+ if ($expectedType
+ && in_array($expectedType, JMicrodata::getExpectedTypes($currentType, $property)))
+ {
+ // Update the current Type
+ $this->handler->setType($expectedType);
+
+ // Display the scope
+ $html .= ' ' . $this->handler->displayScope();
+ }
+
+ return $html;
+ }
+
+ // Check if there is an available 'global' fallback property for the current Type
+ if ($gFallbacks)
+ {
+ foreach ($gFallbacks as $property => $expectedType)
+ {
+ // Check if the property is available in the current Type
+ if (JMicrodata::isPropertyInType($currentType, $property))
+ {
+ $html .= $this->handler->property($property)->display('inline');
+
+ // Check if an expected Type is available
+ if ($expectedType
+ && in_array($expectedType, JMicrodata::getExpectedTypes($currentType, $property)))
+ {
+ // Update the current Type
+ $this->handler->setType($expectedType);
+
+ // Display the scope
+ $html .= ' ' . $this->handler->displayScope();
+ }
+
+ return $html;
+ }
+ }
+ }
+
+ return $html;
+ }
+
+ /**
+ * Find the first data-suffix attribute match available in the node
+ * e.g.