Browse Source

Another couple of functions kept for compatibility purposes

Signed-off-by: emanuele <[email protected]>
emanuele 12 years ago
parent
commit
52c9cdb7eb
1 changed files with 405 additions and 0 deletions
  1. 405 0
      Sources/Subs-Editor.php

+ 405 - 0
Sources/Subs-Editor.php

@@ -909,6 +909,411 @@ function fetchTagAttributes($text)
 	return $attribs;
 }
 
+/**
+ * !!!Compatibility!!!
+ * Attempt to clean up illegal BBC caused by browsers like Opera which don't obey the rules
+ * @param string $text
+ * @return string
+ */
+function legalise_bbc($text)
+{
+	global $modSettings;
+
+	// Don't care about the texts that are too short.
+	if (strlen($text) < 3)
+		return $text;
+
+	// We are going to cycle through the BBC and keep track of tags as they arise - in order. If get to a block level tag we're going to make sure it's not in a non-block level tag!
+	// This will keep the order of tags that are open.
+	$current_tags = array();
+
+	// This will quickly let us see if the tag is active.
+	$active_tags = array();
+
+	// A list of tags that's disabled by the admin.
+	$disabled = empty($modSettings['disabledBBC']) ? array() : array_flip(explode(',', strtolower($modSettings['disabledBBC'])));
+
+	// Add flash if it's disabled as embedded tag.
+	if (empty($modSettings['enableEmbeddedFlash']))
+		$disabled['flash'] = true;
+
+	// Get a list of all the tags that are not disabled.
+	$all_tags = parse_bbc(false);
+	$valid_tags = array();
+	$self_closing_tags = array();
+	foreach ($all_tags as $tag)
+	{
+		if (!isset($disabled[$tag['tag']]))
+			$valid_tags[$tag['tag']] = !empty($tag['block_level']);
+		if (isset($tag['type']) && $tag['type'] == 'closed')
+			$self_closing_tags[] = $tag['tag'];
+	}
+
+	// Don't worry if we're in a code/nobbc.
+	$in_code_nobbc = false;
+
+	// Right - we're going to start by going through the whole lot to make sure we don't have align stuff crossed as this happens load and is stupid!
+	$align_tags = array('left', 'center', 'right', 'pre');
+
+	// Remove those align tags that are not valid.
+	$align_tags = array_intersect($align_tags, array_keys($valid_tags));
+
+	// These keep track of where we are!
+	if (!empty($align_tags) && count($matches = preg_split('~(\\[/?(?:' . implode('|', $align_tags) . ')\\])~', $text, -1, PREG_SPLIT_DELIM_CAPTURE)) > 1)
+	{
+		// The first one is never a tag.
+		$isTag = false;
+
+		// By default we're not inside a tag too.
+		$insideTag = null;
+
+		foreach ($matches as $i => $match)
+		{
+			// We're only interested in tags, not text.
+			if ($isTag)
+			{
+				$isClosingTag = substr($match, 1, 1) === '/';
+				$tagName = substr($match, $isClosingTag ? 2 : 1, -1);
+
+				// We're closing the exact same tag that we opened.
+				if ($isClosingTag && $insideTag === $tagName)
+					$insideTag = null;
+
+				// We're opening a tag and we're not yet inside one either
+				elseif (!$isClosingTag && $insideTag === null)
+					$insideTag = $tagName;
+
+				// In all other cases, this tag must be invalid
+				else
+					unset($matches[$i]);
+			}
+
+			// The next one is gonna be the other one.
+			$isTag = !$isTag;
+		}
+
+		// We're still inside a tag and had no chance for closure?
+		if ($insideTag !== null)
+			$matches[] = '[/' . $insideTag . ']';
+
+		// And a complete text string again.
+		$text = implode('', $matches);
+	}
+
+	// Quickly remove any tags which are back to back.
+	$backToBackPattern = '~\\[(' . implode('|', array_diff(array_keys($valid_tags), array('td', 'anchor'))) . ')[^<>\\[\\]]*\\]\s*\\[/\\1\\]~';
+	$lastlen = 0;
+	while (strlen($text) !== $lastlen)
+		$lastlen = strlen($text = preg_replace($backToBackPattern, '', $text));
+
+	// Need to sort the tags my name length.
+	uksort($valid_tags, 'sort_array_length');
+
+	// These inline tags can compete with each other regarding style.
+	$competing_tags = array(
+		'color',
+		'size',
+	);
+
+	// In case things changed above set these back to normal.
+	$in_code_nobbc = false;
+	$new_text_offset = 0;
+
+	// These keep track of where we are!
+	if (count($parts = preg_split(sprintf('~(\\[)(/?)(%1$s)((?:[\\s=][^\\]\\[]*)?\\])~', implode('|', array_keys($valid_tags))), $text, -1, PREG_SPLIT_DELIM_CAPTURE)) > 1)
+	{
+		// Start with just text.
+		$isTag = false;
+
+		// Start outside [nobbc] or [code] blocks.
+		$inCode = false;
+		$inNoBbc = false;
+
+		// A buffer containing all opened inline elements.
+		$inlineElements = array();
+
+		// A buffer containing all opened block elements.
+		$blockElements = array();
+
+		// A buffer containing the opened inline elements that might compete.
+		$competingElements = array();
+
+		// $i: text, $i + 1: '[', $i + 2: '/', $i + 3: tag, $i + 4: tag tail.
+		for ($i = 0, $n = count($parts) - 1; $i < $n; $i += 5)
+		{
+			$tag = $parts[$i + 3];
+			$isOpeningTag = $parts[$i + 2] === '';
+			$isClosingTag = $parts[$i + 2] === '/';
+			$isBlockLevelTag = isset($valid_tags[$tag]) && $valid_tags[$tag] && !in_array($tag, $self_closing_tags);
+			$isCompetingTag = in_array($tag, $competing_tags);
+
+			// Check if this might be one of those cleaned out tags.
+			if ($tag === '')
+				continue;
+
+			// Special case: inside [code] blocks any code is left untouched.
+			elseif ($tag === 'code')
+			{
+				// We're inside a code block and closing it.
+				if ($inCode && $isClosingTag)
+				{
+					$inCode = false;
+
+					// Reopen tags that were closed before the code block.
+					if (!empty($inlineElements))
+						$parts[$i + 4] .= '[' . implode('][', array_keys($inlineElements)) . ']';
+				}
+
+				// We're outside a coding and nobbc block and opening it.
+				elseif (!$inCode && !$inNoBbc && $isOpeningTag)
+				{
+					// If there are still inline elements left open, close them now.
+					if (!empty($inlineElements))
+					{
+						$parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']';
+						//$inlineElements = array();
+					}
+
+					$inCode = true;
+				}
+
+				// Nothing further to do.
+				continue;
+			}
+
+			// Special case: inside [nobbc] blocks any BBC is left untouched.
+			elseif ($tag === 'nobbc')
+			{
+				// We're inside a nobbc block and closing it.
+				if ($inNoBbc && $isClosingTag)
+				{
+					$inNoBbc = false;
+
+					// Some inline elements might've been closed that need reopening.
+					if (!empty($inlineElements))
+						$parts[$i + 4] .= '[' . implode('][', array_keys($inlineElements)) . ']';
+				}
+
+				// We're outside a nobbc and coding block and opening it.
+				elseif (!$inNoBbc && !$inCode && $isOpeningTag)
+				{
+					// Can't have inline elements still opened.
+					if (!empty($inlineElements))
+					{
+						$parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']';
+						//$inlineElements = array();
+					}
+
+					$inNoBbc = true;
+				}
+
+				continue;
+			}
+
+			// So, we're inside one of the special blocks: ignore any tag.
+			elseif ($inCode || $inNoBbc)
+				continue;
+
+			// We're dealing with an opening tag.
+			if ($isOpeningTag)
+			{
+				// Everyting inside the square brackets of the opening tag.
+				$elementContent = $parts[$i + 3] . substr($parts[$i + 4], 0, -1);
+
+				// A block level opening tag.
+				if ($isBlockLevelTag)
+				{
+					// Are there inline elements still open?
+					if (!empty($inlineElements))
+					{
+						// Close all the inline tags, a block tag is coming...
+						$parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']';
+
+						// Now open them again, we're inside the block tag now.
+						$parts[$i + 5] = '[' . implode('][', array_keys($inlineElements)) . ']' . $parts[$i + 5];
+					}
+
+					$blockElements[] = $tag;
+				}
+
+				// Inline opening tag.
+				elseif (!in_array($tag, $self_closing_tags))
+				{
+					// Can't have two opening elements with the same contents!
+					if (isset($inlineElements[$elementContent]))
+					{
+						// Get rid of this tag.
+						$parts[$i + 1] = $parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = '';
+
+						// Now try to find the corresponding closing tag.
+						$curLevel = 1;
+						for ($j = $i + 5, $m = count($parts) - 1; $j < $m; $j += 5)
+						{
+							// Find the tags with the same tagname
+							if ($parts[$j + 3] === $tag)
+							{
+								// If it's an opening tag, increase the level.
+								if ($parts[$j + 2] === '')
+									$curLevel++;
+
+								// A closing tag, decrease the level.
+								else
+								{
+									$curLevel--;
+
+									// Gotcha! Clean out this closing tag gone rogue.
+									if ($curLevel === 0)
+									{
+										$parts[$j + 1] = $parts[$j + 2] = $parts[$j + 3] = $parts[$j + 4] = '';
+										break;
+									}
+								}
+							}
+						}
+					}
+
+					// Otherwise, add this one to the list.
+					else
+					{
+						if ($isCompetingTag)
+						{
+							if (!isset($competingElements[$tag]))
+								$competingElements[$tag] = array();
+
+							$competingElements[$tag][] = $parts[$i + 4];
+
+							if (count($competingElements[$tag]) > 1)
+								$parts[$i] .= '[/' . $tag . ']';
+						}
+
+						$inlineElements[$elementContent] = $tag;
+					}
+				}
+
+			}
+
+			// Closing tag.
+			else
+			{
+				// Closing the block tag.
+				if ($isBlockLevelTag)
+				{
+					// Close the elements that should've been closed by closing this tag.
+					if (!empty($blockElements))
+					{
+						$addClosingTags = array();
+						while ($element = array_pop($blockElements))
+						{
+							if ($element === $tag)
+								break;
+
+							// Still a block tag was open not equal to this tag.
+							$addClosingTags[] = $element['type'];
+						}
+
+						if (!empty($addClosingTags))
+							$parts[$i + 1] = '[/' . implode('][/', array_reverse($addClosingTags)) . ']' . $parts[$i + 1];
+
+						// Apparently the closing tag was not found on the stack.
+						if (!is_string($element) || $element !== $tag)
+						{
+							// Get rid of this particular closing tag, it was never opened.
+							$parts[$i + 1] = substr($parts[$i + 1], 0, -1);
+							$parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = '';
+							continue;
+						}
+					}
+					else
+					{
+						// Get rid of this closing tag!
+						$parts[$i + 1] = $parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = '';
+						continue;
+					}
+
+					// Inline elements are still left opened?
+					if (!empty($inlineElements))
+					{
+						// Close them first..
+						$parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']';
+
+						// Then reopen them.
+						$parts[$i + 5] = '[' . implode('][', array_keys($inlineElements)) . ']' . $parts[$i + 5];
+					}
+				}
+				// Inline tag.
+				else
+				{
+					// Are we expecting this tag to end?
+					if (in_array($tag, $inlineElements))
+					{
+						foreach (array_reverse($inlineElements, true) as $tagContentToBeClosed => $tagToBeClosed)
+						{
+							// Closing it one way or the other.
+							unset($inlineElements[$tagContentToBeClosed]);
+
+							// Was this the tag we were looking for?
+							if ($tagToBeClosed === $tag)
+								break;
+
+							// Nope, close it and look further!
+							else
+								$parts[$i] .= '[/' . $tagToBeClosed . ']';
+						}
+
+						if ($isCompetingTag && !empty($competingElements[$tag]))
+						{
+							array_pop($competingElements[$tag]);
+
+							if (count($competingElements[$tag]) > 0)
+								$parts[$i + 5] = '[' . $tag . $competingElements[$tag][count($competingElements[$tag]) - 1] . $parts[$i + 5];
+						}
+					}
+
+					// Unexpected closing tag, ex-ter-mi-nate.
+					else
+						$parts[$i + 1] = $parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = '';
+				}
+			}
+		}
+
+		// Close the code tags.
+		if ($inCode)
+			$parts[$i] .= '[/code]';
+
+		// The same for nobbc tags.
+		elseif ($inNoBbc)
+			$parts[$i] .= '[/nobbc]';
+
+		// Still inline tags left unclosed? Close them now, better late than never.
+		elseif (!empty($inlineElements))
+			$parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']';
+
+		// Now close the block elements.
+		if (!empty($blockElements))
+			$parts[$i] .= '[/' . implode('][/', array_reverse($blockElements)) . ']';
+
+		$text = implode('', $parts);
+	}
+
+	// Final clean up of back to back tags.
+	$lastlen = 0;
+	while (strlen($text) !== $lastlen)
+		$lastlen = strlen($text = preg_replace($backToBackPattern, '', $text));
+
+	return $text;
+}
+
+/**
+ * !!!Compatibility!!!
+ * A help function for legalise_bbc for sorting arrays based on length.
+ * @param string $a
+ * @param string $b
+ * @return int 1 or -1
+ */
+function sort_array_length($a, $b)
+{
+	return strlen($a) < strlen($b) ? 1 : -1;
+}
 
 /**
  * Creates the javascript code for localization of the editor (SCEditor)