.
echo $cmsText->processTextLine($text);
echo $cmsText->processTextBlock($text);
//--------------------------------------------------
// End of example setup
***************************************************/
class cmsText {
var $preservedInlineTags;
var $indentLevel;
var $config;
function __construct($config = NULL) {
$this->preservedInlineTags = array();
$this->indentLevel = 1;
$this->setConfig($config);
}
function setConfig($config) {
//--------------------------------------------------
// If this class was not initialised with a config
// array parameter.
if ($config === NULL) {
$config = array();
}
//--------------------------------------------------
// The config setup is an array
$this->config = array();
//--------------------------------------------------
// Boolean (permission) values - default to false
$this->config['allowHtmlCode'] = (isset($config['allowHtmlCode']) && $config['allowHtmlCode'] === true);
$this->config['allowPopupLinks'] = (isset($config['allowPopupLinks']) && $config['allowPopupLinks'] === true);
$this->config['allowMailLinks'] = (isset($config['allowMailLinks']) && $config['allowMailLinks'] === true);
$this->config['allowImgTags'] = (isset($config['allowImgTags']) && $config['allowImgTags'] === true);
$this->config['allowParaAlign'] = (isset($config['allowParaAlign']) && $config['allowParaAlign'] === true);
$this->config['allowListTags'] = (isset($config['allowListTags']) && $config['allowListTags'] === true);
$this->config['allowTableTags'] = (isset($config['allowTableTags']) && $config['allowTableTags'] === true);
$this->config['allowHeadingTags'] = (isset($config['allowHeadingTags']) && $config['allowHeadingTags'] === true);
$this->config['noFollowLinks'] = (isset($config['noFollowLinks']) && $config['noFollowLinks'] === true);
$this->config['hideCmsComments'] = (isset($config['hideCmsComments']) && $config['hideCmsComments'] === true);
$this->config['plainTextMailLinks'] = (isset($config['plainTextMailLinks']) && $config['plainTextMailLinks'] === true);
//--------------------------------------------------
// General config
if (isset($config['headingLevel'])) {
$this->config['headingLevel'] = $config['headingLevel']; // Valid number checked later
} else {
$this->config['headingLevel'] = NULL;
}
}
function changeConfig($key, $value) {
if ($key == 'headingLevel') {
$this->config['headingLevel'] = $value;
} else if (isset($this->config[$key])) {
$this->config[$key] = ($value === true);
}
}
function processTextLine($string, $preserveOpenTags = false, $childOfTag = NULL) { // Do not set 'childOfTag', its considered PRIVATE
//--------------------------------------------------
// Make the string HTML safe and remove whitespace
// at the end.
// NOTE: If this is a child of a tag,
// then the content would have already
// been html encoded.
if ($childOfTag === NULL) {
$string = rtrim(html($string));
}
//--------------------------------------------------
// Defaults
$inTagText = NULL;
$inTagAttribute = NULL;
$escapingStack = 0;
$tagStack = array();
$outputHtml = '';
$inlineTags = array();
$inlineTags['b']['open'] = '
';
$inlineTags['b']['close'] = '';
$inlineTags['i']['open'] = '
';
$inlineTags['i']['close'] = '';
//--------------------------------------------------
// If any tags were preserved from the last run,
// then re-open them
if ($preserveOpenTags === true) {
while ($tag = array_pop($this->preservedInlineTags)) {
$tagStack[] = $tag;
$outputHtml .= $inlineTags[$tag]['open'];
}
}
//--------------------------------------------------
// Loop though each of the characters in the string
$stringLength = strlen($string);
for ($i = 0; $i < $stringLength; $i++) {
$char = $string[$i];
if ($char == '[' && $inTagAttribute === NULL) {
//--------------------------------------------------
// If we are already collecting data for a tag, which
// has not finished yet, then then its not really a
// tag... the user has just used "["
if ($inTagText !== NULL) {
$tmp = str_repeat('\\', $escapingStack) . '[' . $inTagText;
$result = preg_match('/^(.*?)(\\\\*)$/', $tmp, $matches);
if ($result) {
$outputHtml .= $matches[1];
$escapingStack = strlen($matches[2]); // Slashes before the new "["
} else {
$outputHtml .= $tmp;
$escapingStack = 0;
}
}
//--------------------------------------------------
// Remember we are now in tag mode (not NULL)
$inTagText = '';
} else if ($inTagText !== NULL) {
if ($char == ']' && $inTagAttribute === NULL) {
//--------------------------------------------------
// The tag text has been collected, so now extract
// the tag parts
$result = preg_match('/^(\/?)([^ ]*)( +.*)?$/', $inTagText, $matches);
if ($result) {
$tagTypeOpener = ($matches[1] == '');
$tagNameLower = strtolower($matches[2]);
$tagAttributes = (isset($matches[3]) ? $matches[3] : '');
} else {
$tagTypeOpener = false;
$tagNameLower = '';
$tagAttributes = '';
}
//--------------------------------------------------
// Process the tag (if ness) by setting the
// $tagOutput to something other than NULL
$tagOutput = NULL;
$tagEscaped = (($escapingStack % 2) != 0);
$tagValid = isset($inlineTags[$tagNameLower]);
if ($tagValid && $tagAttributes != '') {
$tagValid = false; // Inline tags arn't allowed attributes
}
if ($tagValid && $tagEscaped != true) {
if ($tagTypeOpener) {
//--------------------------------------------------
// This this is an opening tag, if it is not already
// open AND this is not at the end of the string,
// then open it now... otherwise we cannot open it
// again, so silently ignore it.
if (!in_array($tagNameLower, $tagStack) && (($i + 1) < $stringLength)) {
$tagOutput = $inlineTags[$tagNameLower]['open'];
$tagStack[] = $tagNameLower;
} else {
$tagOutput = '';
}
} else if (!in_array($tagNameLower, $tagStack)) {
//--------------------------------------------------
// This is a CLOSING tag that is NOT open, so
// silently ignore it.
$tagOutput = '';
} else {
//--------------------------------------------------
// This is a CLOSING tag that is already open
$tagOutput = '';
$tempStack = array();
//--------------------------------------------------
// Close all of the tags which are currently open,
// until we find the one the user has requested that
// we close (XML formatting).
$processing = true;
while ($processing) {
$tag = array_pop($tagStack);
$tagOutput .= $inlineTags[$tag]['close'];
if ($tag == $tagNameLower) {
$processing = false;
} else {
$tempStack[] = $tag;
}
}
//--------------------------------------------------
// Reopen any of the tags which were closed in order
// to meet the users requested tag close.
while ($tag = array_pop($tempStack)) {
$tagStack[] = $tag;
$tagOutput .= $inlineTags[$tag]['open'];
}
}
} else {
//--------------------------------------------------
// This might be an inline element, so extract
// the attributes
$tagAttrib1 = '';
$tagAttrib2 = '';
$tagAttrib3 = '';
$found = preg_match('/^ +("|')(.*?)\1(?: +\1(.*?)\1)?(?: +\1(.*?)\1)?$/', $tagAttributes, $matches);
if ($found) {
//--------------------------------------------------
// Attribute 1
$tagAttrib1 = $matches[2];
//--------------------------------------------------
// Attribute 2
// NOTE: Due to inTagAttribute, a child tag might
// appear within this attribute. However, we cannot
// preserveOpenTags, as we would need to re-open
// them blindly, potentially creating XML errors.
if (isset($matches[3])) {
$tagAttrib2 = $matches[3];
if ($tagNameLower == 'link' || $tagNameLower == 'open' || $tagNameLower == 'mail') {
$tagAttrib2 = $this->processTextLine($tagAttrib2, false, true);
}
}
//--------------------------------------------------
// Attribute 3
if (isset($matches[4])) {
$tagAttrib3 = $matches[4];
}
}
//--------------------------------------------------
// If this is a recognised inline element, replace
// it with the relevant HTML
// NOTE: If we are processing some text which is
// a child of a tag, then it should be a child
// of a link... and as such, we don't to allow
// and of the following link tags.
if ($tagAttrib1 != '') {
if ($tagAttrib3 != '') {
$classHtml = ' class="' . $tagAttrib3 . '"';
} else {
$classHtml = '';
}
if ($tagNameLower == 'link' && $childOfTag === NULL) {
$tagValid = true;
if ($tagEscaped != true) {
if ($tagAttrib2 == '') {
$tagOutput = '
config['noFollowLinks'] ? ' rel="nofollow"' : '') . '>' . $tagAttrib1 . '';
} else {
$tagOutput = '
config['noFollowLinks'] ? ' rel="nofollow"' : '') . '>' . $tagAttrib2 . '';
}
}
} else if ($tagNameLower == 'open' && $this->config['allowPopupLinks'] && $childOfTag === NULL) {
$tagValid = true;
if ($tagEscaped != true) {
if ($tagAttrib2 == '') {
$tagOutput = '
config['noFollowLinks'] ? ' rel="nofollow"' : '') . '>' . $tagAttrib1 . '';
} else {
$tagOutput = '
config['noFollowLinks'] ? ' rel="nofollow"' : '') . '>' . $tagAttrib2 . '';
}
}
} else if ($tagNameLower == 'mail' && $this->config['allowMailLinks'] && $childOfTag === NULL) {
$tagValid = true;
if ($tagEscaped != true) {
if ($this->config['plainTextMailLinks']) {
if ($tagAttrib2 == '') {
$tagOutput = '
' . $tagAttrib1 . '';
} else {
$tagOutput = '
' . $tagAttrib2 . '';
}
} else {
if ($tagAttrib2 == '') {
$tagOutput = '
' . $tagAttrib1 . '';
} else {
$tagOutput = '
' . $tagAttrib2 . '';
}
}
}
} else if ($tagNameLower == 'img' && $this->config['allowImgTags']) {
$tagValid = true;
if ($tagEscaped != true) {
if ($tagAttrib2 == '' && $tagAttrib3 == '') {
$tagOutput = '
';
} else if ($tagAttrib3 == '') {
$tagOutput = '
';
} else {
$tagOutput = '
';
}
}
}
} else if ($tagNameLower == 'hr') {
$tagValid = true;
$tagOutput = '
';
}
}
//--------------------------------------------------
// If the tag is invalid, or was escaped, return
// its text value to the $outputHtml
if ($tagValid) {
$escapingStack = floor($escapingStack / 2);
}
if ($tagOutput === NULL) {
$tagOutput = '[' . $inTagText . ']'; // Invalid tag, or escaped tag
}
$outputHtml .= str_repeat('\\', $escapingStack) . $tagOutput;
//--------------------------------------------------
// Reset the tag tracking variables
$inTagText = NULL;
$escapingStack = 0;
} else {
//--------------------------------------------------
// The tag has not finished yet, so keep stacking
// its contents up.
$inTagText .= $char;
//--------------------------------------------------
// Detect if we are in an attribute
$marker = substr($inTagText, -6);
if ($marker == '"') $marker = '"';
if ($marker == ''') $marker = "'";
if ($inTagAttribute === NULL) {
if ($marker == '"' || $marker == "'") {
$inTagAttribute = $marker;
}
} else {
if ($inTagAttribute == $marker) {
$inTagAttribute = NULL;
}
}
}
} else if ($char == '\\') {
//--------------------------------------------------
// Add to the escaping string stack... these should
// be added to the $outputHtml later
$escapingStack++;
} else {
//--------------------------------------------------
// We are looking at an ordinary character which
// is not part of a tag, so add the relevant number
// of escaping characters which have been "ignored"
// just incase we could hit a tag.
if ($escapingStack > 0) {
$outputHtml .= str_repeat('\\', $escapingStack);
$escapingStack = 0;
}
//--------------------------------------------------
// Store the character into the output buffer,
// although if a new line is being generated, use
// a simple
... if its a double newline that
// should have been handled by blockLevelTagClose()
if ($char == "\n") {
if ($i > 0 && ($i + 1) < $stringLength) { // Don't process the first and last "\n"
$outputHtml .= "
\n";
}
} else {
$outputHtml .= $char;
}
}
}
//--------------------------------------------------
// If requested, remember the tags which were left
// open (about to be closed) so they can be reopened
// next time this function is called.
$this->preservedInlineTags = $tagStack;
//--------------------------------------------------
// If there is any text remaining in the tag buffer,
// dump it into $outputHtml
if ($escapingStack > 0) {
$outputHtml .= str_repeat('\\', $escapingStack);
}
if ($inTagText !== NULL) {
$outputHtml .= '[' . $inTagText;
}
while ($tag = array_pop($tagStack)) {
$outputHtml .= $inlineTags[$tag]['close'];
}
//--------------------------------------------------
// Post process - remove any tags which have no
// effect on the output (surround white-space). Not
// ness, but it keeps the output cleaner and stops
// upsetting a few code checkers
$reRun = 0;
$reRunMax = count($inlineTags);
while ($reRun++ < $reRunMax) {
foreach ($inlineTags as $tagInfo) {
$regExpOpen = preg_quote($tagInfo['open'], '/');
$regExpClose = preg_quote($tagInfo['close'], '/');
$outputHtml = preg_replace('/' . $regExpOpen . '(\s*)' . $regExpClose . '/i', '$1', $outputHtml, -1);
$outputHtml = preg_replace('/' . $regExpClose . '(\s*)' . $regExpOpen . '/i', '$1', $outputHtml, -1);
}
}
//--------------------------------------------------
// Encode the whitespace characters so browsers
// will render them
$outputHtml = str_replace(' ', ' ', $outputHtml); // 2 spaces
$outputHtml = str_replace(chr(9), ' ', $outputHtml);
//--------------------------------------------------
// Set the indent level - again, for code checkers
$outputHtml = str_replace("\n", "\n" . str_repeat("\t", $this->indentLevel), $outputHtml);
//--------------------------------------------------
// Return the output
return $outputHtml;
}
function processTextBlock($string, $preserveOpenTags = false) {
//--------------------------------------------------
// Ensure were using use the proper UNIX "\n"
$string = str_replace("\r\n", "\n", $string);
//--------------------------------------------------
// A new block, so we should not care about the
// previous indent level
$this->indentLevel = 1;
//--------------------------------------------------
// Defaults
$inTagText = NULL;
$escapingStack = 0;
$currentBlockTag = 'p';
$outputBuffer = '';
$outputHtml = '';
$blockLevelTags = array();
$blockLevelTags[] = 'p';
if ($this->config['allowParaAlign']) {
$blockLevelTags[] = 'left';
$blockLevelTags[] = 'center';
$blockLevelTags[] = 'right';
}
if ($this->config['allowHeadingTags']) {
$blockLevelTags[] = 'h';
$blockLevelTags[] = 'h1';
$blockLevelTags[] = 'h2';
$blockLevelTags[] = 'h3';
$blockLevelTags[] = 'h4';
$blockLevelTags[] = 'h5';
$blockLevelTags[] = 'h6';
}
if ($this->config['allowListTags']) {
$blockLevelTags[] = 'list';
}
if ($this->config['allowTableTags']) {
$blockLevelTags[] = 'table';
}
if ($this->config['allowHtmlCode']) {
$blockLevelTags[] = 'html';
}
//--------------------------------------------------
// Loop though each of the characters in the string
$stringLength = strlen($string);
for ($i = 0; $i < $stringLength; $i++) {
$char = $string[$i];
if ($char == '[') {
//--------------------------------------------------
// If we are already collecting data for a tag, which
// has not finished yet, then then its not really a
// tag... the user has just used "["
if ($inTagText !== NULL) {
$tmp = str_repeat('\\', $escapingStack) . '[' . $inTagText;
$result = preg_match('/^(.*?)(\\\\*)$/', $tmp, $matches);
if ($result) {
$outputBuffer .= $matches[1];
$escapingStack = strlen($matches[2]); // Slashes before the new "["
} else {
$outputBuffer .= $tmp;
$escapingStack = 0;
}
}
//--------------------------------------------------
// Remember we are now in tag mode (not NULL)
$inTagText = '';
} else if ($inTagText !== NULL) {
if ($char == ']') {
//--------------------------------------------------
// Extract the tag parts
$result = preg_match('/^(\/?)([^ ]*)$/', $inTagText, $matches);
if ($result) {
$tagTypeOpener = ($matches[1] == '');
$tagNameLower = strtolower($matches[2]);
} else {
$tagTypeOpener = false;
$tagNameLower = '';
}
//--------------------------------------------------
// Default processing variables
$tagOutput = NULL;
$tagIgnored = false;
if (in_array($tagNameLower, $blockLevelTags)) { // If this is a valid tag
if (($escapingStack % 2) == 0) { // Tag not escaped, with an odd number of slashes
if ($tagTypeOpener == true) {
if ($tagNameLower != $currentBlockTag) {
$tagOutput .= $this->blockLevelTagClose($currentBlockTag, $outputBuffer, true);
$currentBlockTag = $tagNameLower;
} else {
$tagIgnored = true; // Already open
}
} else {
if ($tagNameLower == $currentBlockTag) {
$tagOutput = $this->blockLevelTagClose($currentBlockTag, $outputBuffer, true);
$currentBlockTag = 'p'; // Return to default
} else {
$tagIgnored = true; // Already closed
}
}
}
$escapingStack = floor($escapingStack / 2);
}
//--------------------------------------------------
// If tag output was generated add it to outputHtml,
// otherwise dump the (invalid) tag back in to the
// outputBuffer
if ($tagOutput === NULL) {
$outputBuffer .= str_repeat('\\', $escapingStack) . ($tagIgnored ? '' : '[' . $inTagText . ']'); // Invalid tag, or escaped tag
} else {
$outputHtml .= str_repeat('\\', $escapingStack) . $tagOutput;
$outputBuffer = '';
}
//--------------------------------------------------
// Reset the tag tracking variables
$inTagText = NULL;
$escapingStack = 0;
} else {
//--------------------------------------------------
// The tag has not finished yet, so keep stacking
// its contents up.
$inTagText .= $char;
}
} else if ($char == '\\') {
//--------------------------------------------------
// Add to the escaping string stack... these should
// be added to the $outputBuffer later
$escapingStack++;
} else {
//--------------------------------------------------
// We are looking at an ordinary character which
// is not part of a tag, so add the relevant number
// of escaping characters which have been "ignored"
// just incase we could hit a tag.
if ($escapingStack > 0) {
$outputBuffer .= str_repeat('\\', $escapingStack);
$escapingStack = 0;
}
//--------------------------------------------------
// Store the character into the output buffer.
$outputBuffer .= $char;
}
}
//--------------------------------------------------
// If there is any text remaining in the tag buffer,
// dump it into $outputHtml
if ($escapingStack > 0) {
$outputBuffer .= str_repeat('\\', $escapingStack);
}
if ($inTagText !== NULL) {
$outputBuffer .= '[' . $inTagText;
}
$outputHtml .= $this->blockLevelTagClose($currentBlockTag, $outputBuffer, $preserveOpenTags); // By default, tags are not preserved
//--------------------------------------------------
// Return the output
if ($this->config['hideCmsComments']) {
return $outputHtml;
} else {
return "\n" . $outputHtml . "\n\n";
}
}
function blockLevelTagClose($currentBlockTag, $content, $preserveOpenTags = false) {
//--------------------------------------------------
// If the tag is empty, there is no point generating
// any html output
if (trim($content) == '') {
return '';
}
//--------------------------------------------------
// Process the relevant tags
if ($currentBlockTag == 'p' || $currentBlockTag == 'left' || $currentBlockTag == 'center' || $currentBlockTag == 'right') {
//--------------------------------------------------
// Determine text alignment (none by default)
if ($currentBlockTag == 'p') {
$align = NULL;
} else {
$align = $currentBlockTag;
}
//--------------------------------------------------
// If double newlines are used, split into
// multiple paragraphs
$htmlOutput = '';
$subParas = explode("\n\n", $content);
$subParasLength = count($subParas);
$k = 1;
foreach ($subParas as $subPara) {
if (trim($subPara) != '') {
$htmlOutput .= "\n" . $this->processParagraph($subPara, ($k == 1 ? $preserveOpenTags : true), $align);
}
$k++;
}
//--------------------------------------------------
// Return the output
return $htmlOutput;
} else if (preg_match('/^h([1-6])?$/', $currentBlockTag, $matches)) {
$level = intval($this->config['headingLevel']);
if (isset($matches[1])) {
$level += (intval($matches[1]) - 1);
}
if ($level < 1) $level = 1;
if ($level > 6) $level = 6;
return "\n" . $this->processHeading($content, $preserveOpenTags, $level);
} else if ($currentBlockTag == 'list') {
return "\n" . $this->processList($content, $preserveOpenTags);
} else if ($currentBlockTag == 'table') {
return "\n" . $this->processTable($content, $preserveOpenTags);
} else if ($currentBlockTag == 'html') {
if ($this->config['hideCmsComments']) {
return $content;
} else {
return "\n\n" . $content . "\n";
}
}
}
function processParagraph($content, $preserveOpenTags, $align = NULL) {
//--------------------------------------------------
// Process the content, but have it correctly
// indented if its multi-lined
$itemHtml = $this->processTextLine($content, $preserveOpenTags);
if (strpos($itemHtml, "\n") !== false) {
$itemHtml = "\n\t" . $itemHtml . "\n";
}
//--------------------------------------------------
// Return the output in its wrapper
return '
' . $itemHtml . '
';
}
function processHeading($content, $preserveOpenTags, $level) {
//--------------------------------------------------
// Process the content, but have it correctly
// indented if its multi-lined
$itemHtml = $this->processTextLine($content, $preserveOpenTags);
if (strpos($itemHtml, "\n") !== false) {
$itemHtml = "\n\t" . $itemHtml . "\n";
}
//--------------------------------------------------
// Return the output in its wrapper
return '
' . $itemHtml . '';
}
function processList($content, $preserveOpenTags) {
//--------------------------------------------------
// Increase the indent level
$this->indentLevel++;
//--------------------------------------------------
// Process each item in the list
$outputHtml = '';
$items = preg_split('/\n(\*|#)/', $content);
foreach ($items as $item) {
$item = trim($item);
if ($item != '') {
$itemHtml = $this->processTextLine($item, $preserveOpenTags);
if (strpos($itemHtml, "\n") !== false) {
$itemHtml = "\n\t\t" . $itemHtml . "\n\t";
}
$outputHtml .= "\n\t" . '
' . $itemHtml . '';
}
}
//--------------------------------------------------
// Restore the indent level
$this->indentLevel--;
//--------------------------------------------------
// Return the output in its wrapper
if (preg_match('/^\s*#/', $content)) {
return '
' . $outputHtml . "\n" . '
';
} else {
return '
' . $outputHtml . "\n" . '
';
}
}
function processTable($content, $preserveOpenTags) {
//--------------------------------------------------
// Split the data into a multi-dimensional array
$tableColumns = 1;
$tableData = array();
$tableRowCells = array();
$k = 0;
$rows = explode("\n", $content);
foreach ($rows as $row) {
if ($row != '') {
$cells = explode('|', $row);
$tableData[$k] = $cells;
$tableRowCells[$k] = count($cells);
if ($tableRowCells[$k] > $tableColumns) {
$tableColumns = $tableRowCells[$k];
}
$k++;
}
}
//--------------------------------------------------
// Build the html output
$k = 0;
$inHead = true;
$outputHeadHtml = '';
$outputBodyHtml = '';
foreach ($tableData as $cells) {
$rowHtml = "\n\t\t
";
$i = 0;
foreach ($cells as $cellData) {
$i++;
if ($i == 1 && $inHead) {
if (substr($cellData, 0, 1) == '#') {
$cellData = substr($cellData, 1);
} else {
$inHead = false;
}
}
$colSpan = (($tableColumns + 1) - $tableRowCells[$k]);
$rowHtml .= "\n\t\t\t" . '<' . ($inHead ? 'th' : 'td') . ($colSpan > 1 && $i == $tableRowCells[$k] ? ' colspan="' . intval($colSpan) . '"' : '') . '>' . $this->processTextLine($cellData, $preserveOpenTags) . ($inHead ? '' : '');
}
$rowHtml .= "\n\t\t
";
if ($inHead) {
$outputHeadHtml .= $rowHtml;
} else {
$outputBodyHtml .= $rowHtml;
}
$k++;
}
//--------------------------------------------------
// Return the output in its wrapper
return "
\n" . ($outputHeadHtml == '' ? '' : "\t" . $outputHeadHtml . "\n\t\n") . ($outputBodyHtml == '' ? '' : "\t" . $outputBodyHtml . "\n\t\n") . "
";
}
}
//--------------------------------------------------
// Copyright (c) 2006, Craig Francis All rights
// reserved.
//
// Redistribution and use in source and binary forms,
// with or without modification, are permitted provided
// that the following conditions are met:
//
// * Redistributions of source code must retain the
// above copyright notice, this list of
// conditions and the following disclaimer.
// * Redistributions in binary form must reproduce
// the above copyright notice, this list of
// conditions and the following disclaimer in the
// documentation and/or other materials provided
// with the distribution.
// * Neither the name of the copyright holder nor the
// names of its contributors may be used to endorse
// or promote products derived from this software
// without specific prior written permission.
//
// This software is provided by the copyright holders
// and contributors "as is" and any express or implied
// warranties, including, but not limited to, the
// implied warranties of merchantability and fitness
// for a particular purpose are disclaimed. In no event
// shall the copyright holder or contributors be liable
// for any direct, indirect, incidental, special,
// exemplary, or consequential damages (including, but
// not limited to, procurement of substitute goods or
// services; loss of use, data, or profits; or business
// interruption) however caused and on any theory of
// liability, whether in contract, strict liability, or
// tort (including negligence or otherwise) arising in
// any way out of the use of this software, even if
// advised of the possibility of such damage.
//--------------------------------------------------
?>