diff -ru typo3_src-4.2.0RC1.orig/typo3/sysext/cms/tslib/class.tslib_content.php typo3_src-4.2.0RC1.new/typo3/sysext/cms/tslib/class.tslib_content.php --- typo3_src-4.2.0RC1.orig/typo3/sysext/cms/tslib/class.tslib_content.php 2008-04-01 23:36:30.000000000 +0200 +++ typo3_src-4.2.0RC1.new/typo3/sysext/cms/tslib/class.tslib_content.php 2008-04-10 11:56:05.000000000 +0200 @@ -3667,25 +3667,96 @@ * @access private * @see stdWrap() */ - function crop($content,$options) { + function crop($content,$options) { $options = explode('|',$options); $chars = intval($options[0]); - $afterstring = trim($options[1]); - $crop2space = trim($options[2]); - if ($chars) { - if (strlen($content)>abs($chars)) { - if ($chars<0) { - $content = $GLOBALS['TSFE']->csConvObj->substr($GLOBALS['TSFE']->renderCharset,$content,$chars); - $trunc_at = strpos($content, ' '); - $content = ($trunc_at&&$crop2space) ? $afterstring.substr($content,$trunc_at) : $afterstring.$content; + $replacementForEllipsis = trim($options[1]); + $crop2space = isset($options[2]) ? trim($options[2]) : 0; + + // split $content into an array (even items in the array are outside the tags, odd numbers are tag-blocks) + $tags= 'a|b|blockquote|body|div|em|font|form|h1|h2|h3|h4|h5|h6|i|li|map|ol|option|p|pre|sub|sup|select|span|strong|table|td|textarea|tr|u|ul|br|hr|img|input|area|link'; + $tagsRegEx = " + ( + (?: + + ) + | + + \".*?\" + | + '.*?' + | + [^'\">\s]+ + ) + )? + )+ + \s*|\s* + ) + /?> + )"; + $splittedContent = preg_split("#" . $tagsRegEx . "#ux", $content, -1, PREG_SPLIT_DELIM_CAPTURE); + // reverse array if we are cropping from right + if ($chars < 0) $splittedContent = array_reverse($splittedContent); + + // crop the text (chars of tag-blocks are not counted) + $strLen = 0; + $croppedOffset = NULL; // offset of the content item which was cropped + for ($offset = 0; $offset < count($splittedContent); $offset++) { + if ($offset%2 === 0) { + $thisStrLen = $GLOBALS['TSFE']->csConvObj->strlen($GLOBALS['TSFE']->renderCharset, html_entity_decode($splittedContent[$offset],ENT_COMPAT,$GLOBALS['TSFE']->renderCharset)); + if (($strLen + $thisStrLen > abs($chars))) { + $croppedOffset = $offset; + $cropPosition = abs($chars) - $strLen; + if ($crop2space) { + $cropRegEx = $chars < 0 ? '#(?<=\s).{0,' . $cropPosition . '}$#ui' : '#^.{0,' . $cropPosition . '}(?=\s)#ui'; + } else { + $cropRegEx = $chars < 0 ? '#(.(?![^&\s]{2,7};)|(&[^&\s;]{2,7};)){0,' . $cropPosition . '}$#ui' : '#^(.(?![^&\s]{2,7};)|(&[^&\s;]{2,7};)){0,' . $cropPosition . '}#ui'; + } + if (preg_match($cropRegEx, $splittedContent[$offset], $croppedMatch)) $splittedContent[$offset] = $croppedMatch[0]; + break; } else { - $content = $GLOBALS['TSFE']->csConvObj->substr($GLOBALS['TSFE']->renderCharset,$content,0,$chars); - $trunc_at = strrpos($content, ' '); - $content = ($trunc_at&&$crop2space) ? substr($content, 0, $trunc_at).$afterstring : $content.$afterstring; + $strLen += $thisStrLen; } } } - return $content; + + // close cropped tags + $closingTags = array(); + if($croppedOffset !== NULL) { + $tagName = ''; + $openingTagRegEx = '#^<(\w+)(?:\s|>)#u'; + $closingTagRegEx = '#^)#u'; + for ($offset=$croppedOffset-1; $offset >= 0; $offset = $offset-2) { + if (preg_match('&/>$&', $splittedContent[$offset])) continue; // ignore empty element tags (e.g.
) + preg_match($chars < 0 ? $closingTagRegEx : $openingTagRegEx, $splittedContent[$offset], $matches); + $tagName = isset($matches[1]) ? $matches[1] : NULL; + if ($tagName !== NULL) { + // seek for the closing (or opening) tag + $seekingTagName = ''; + for ($seekingOffset = $offset + 2; $seekingOffset < count($splittedContent); $seekingOffset = $seekingOffset + 2) { + preg_match($chars < 0 ? $openingTagRegEx : $closingTagRegEx, $splittedContent[$seekingOffset], $matches); + $seekingTagName = isset($matches[1]) ? $matches[1] : NULL; + if ($tagName === $seekingTagName) { // we found a matching tag + // add closing tag only if it occurs after the cropped content item + if ($seekingOffset > $croppedOffset) $closingTags[] = $splittedContent[$seekingOffset]; + break; + } + } + } + } + array_splice($splittedContent, $croppedOffset + 1); // drop the cropped items of the content array + } + $splittedContent = array_merge($splittedContent, array($croppedOffset !== NULL ? $replacementForEllipsis : ''), $closingTags); + // reverse array once again if we are cropping from the end + if ($chars < 0) $splittedContent = array_reverse($splittedContent); + + return implode('', $splittedContent); } /**