Correct news body text:
- save only text between start and end tags
- correct path to embed files according to $transTable
@param string $text
@param array $transTable
@return string
355437
public static function preLoadText($text, $transTable = [])
    {
        $module = Module::getModuleByClassname(Module::className());
        $editorContentHelper = $module->contentHelper;

        $text = trim($text);
        if (empty($text)) return '';

        // exstract text between start and end tags
        $pos = strpos($text, static::START_TEXT_TAG);
        if ($pos !== false) {
            $pos += strlen(static::START_TEXT_TAG);
            $text = substr($text, $pos);
        }
        $pos = strpos($text, static::END_TEXT_TAG);
        if ($pos !== false) {
            $text = substr($text, 0, $pos);
        }

        // correct files path
        $text = strtr($text, $transTable);
        $text = $editorContentHelper::beforeSaveBody($text); // convert realpath to '@uploads/.../ID'

        //var_dump($text);exit;
        return $text;
    }
325475
public function splitTag(string $text, string $tag): array
    {
        if (!$tag) {
            return [$text];
        }

        return preg_split('/(?=<'.$tag.'([^>])*>)/', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) ?: [$text];
    }
343037
function WriteText($x, $y, $txt)
	{
		// Output a string using Text() but does encoding and text reversing of RTL
		$txt = $this->purify_utf8_text($txt);
		if ($this->text_input_as_HTML) {
			$txt = $this->all_entities_to_utf8($txt);
		}
		if ($this->usingCoreFont) {
			$txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
		}

		// DIRECTIONALITY
		if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
			$this->biDirectional = true;
		} // *OTL*

		$textvar = 0;
		$save_OTLtags = $this->OTLtags;
		$this->OTLtags = [];
		if ($this->useKerning) {
			if ($this->CurrentFont['haskernGPOS']) {
				$this->OTLtags['Plus'] .= ' kern';
			} else {
				$textvar = ($textvar | TextVars::FC_KERNING);
			}
		}

		/* -- OTL -- */
		// Use OTL OpenType Table Layout - GSUB & GPOS
		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
			$txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
			$OTLdata = $this->otl->OTLdata;
		}
		/* -- END OTL -- */
		$this->OTLtags = $save_OTLtags;

		$this->magic_reverse_dir($txt, $this->directionality, $OTLdata);

		$this->Text($x, $y, $txt, $OTLdata, $textvar);
	}
992651
function IndexEntrySee($txta, $txtb)
	{
		if ($this->directionality == 'rtl') { // *OTL*
			// ONLY DO THIS IF NOT IN TAGS
			if ($txta == strip_tags($txta)) {
				$txta = str_replace(':', ' - ', $txta); // *OTL*
			}
			if ($txtb == strip_tags($txtb)) {
				$txtb = str_replace(':', ' - ', $txtb); // *OTL*
			}
		} // *OTL*
		else { // *OTL*
			if ($txta == strip_tags($txta)) {
				$txta = str_replace(':', ', ', $txta);
			}
			if ($txtb == strip_tags($txtb)) {
				$txtb = str_replace(':', ', ', $txtb);
			}
		} // *OTL*
		$this->Reference[] = ['t' => $txta . ' - see ' . $txtb, 'p' => []];
	}
1318909
public function encode($text)
    {
        //计算需要填充的位数
        $padAmount = $this->blockSize - (strlen($text) % $this->blockSize);

        $padAmount = $padAmount !== 0 ? $padAmount : $this->blockSize;

        //获得补位所用的字符
        $padChr = chr($padAmount);

        $tmp = '';

        for ($index = 0; $index < $padAmount; $index++) {
            $tmp .= $padChr;
        }

        return $text.$tmp;
    }
847971
static private function borkify( $text )
    {
        $textBlocks = preg_split( '/(%[^ ]+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
        $newTextBlocks = array();
        foreach ( $textBlocks as $text )
        {
            if ( strlen( $text ) && $text[0] == '%' )
            {
                $newTextBlocks[] = (string) $text;
                continue;
            }

            $orgtext = $text;

            $searchMap = array(
                '/au/', '/\Bu/', '/\Btion/', '/an/', '/a\B/', '/en\b/',
                '/\Bew/', '/\Bf/', '/\Bir/', '/\Bi/', '/\bo/', '/ow/', '/ph/',
                '/th\b/', '/\bU/', '/y\b/', '/v/', '/w/', '/ooo/',
            );
            $replaceMap = array(
                'oo', 'oo', 'shun', 'un', 'e', 'ee',
                'oo', 'ff', 'ur', 'ee', 'oo', 'oo', 'f',
                't', 'Oo', 'ai', 'f', 'v', 'oo',
            );
            $text = preg_replace( $searchMap, $replaceMap, $text );

            if ( $orgtext == $text && count( $newTextBlocks ) )
            {
                $text .= '-a';
            }
            $newTextBlocks[] = (string) $text;
        }
        $text = implode( '', $newTextBlocks );
        $text = preg_replace( '/([:.?!])(.*)/', '\\2\\1', $text );
        return "[$text]";
    }
604619
protected function parseAttr($text)
    {
        $atts = [];

        $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';

        $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);

        if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) )
        {
            foreach ($match as $m)
            {
                if (!empty($m[1]))
                    $atts[strtolower($m[1])] = stripcslashes($m[2]);
                elseif (!empty($m[3]))
                    $atts[strtolower($m[3])] = stripcslashes($m[4]);
                elseif (!empty($m[5]))
                    $atts[strtolower($m[5])] = stripcslashes($m[6]);
                elseif (isset($m[7]) and strlen($m[7]))
                    $atts[] = stripcslashes($m[7]);
                elseif (isset($m[8]))
                    $atts[] = stripcslashes($m[8]);
            }
        }
        else
        {
            $atts = ltrim($text);
        }

        return $atts;
    }
738707
protected function parse($text) {
        // Build tree structure
        $in = array();
        for ($ptr=0,$len=strlen($text),$tree=array(),$node=&$tree,
            $stack=array(),$depth=0,$tmp='';$ptr<$len;)
            if (preg_match('/^@(end)?('.$this->tags.')\b(.*)/i', substr($text,$ptr),$match)) {
                $single = substr($match[3], strlen($match[3])-1)==='@';
                !$single || $match[3] = substr($match[3], 0, strlen($match[3])-1);
                if (strlen($tmp))
                    $node[] = $tmp;
                // Element node
                if ($match[1]) {
                    if ($in) {
                        // in element
                        for ($i=count($in)-1; $i > -1 ; --$i)
                            $in[$i][1] = array_pop($node);
                        $node['@in'] = $in;
                        $in = array();
                    }
                    // Find matching start tag
                    $save  = $depth;
                    $found = false;
                    while ($depth>0) {
                        $depth--;
                        foreach ($stack[$depth] as $item)
                            if (is_array($item) && isset($item[$match[2]])) {
                                // Start tag found
                                $found = true;
                                break 2;
                            }
                    }
                    if (!$found)
                        // Unbalanced tag
                        $depth = $save;
                    $node =& $stack[$depth];
                }
                elseif ($this->intag($match[2])) {
                    $in[] = array($match[2], '@attrib'=>$this->parseAttrib($match[3]), '');
                }
                else {
                    // Start tag
                    $stack[$depth]   =& $node;
                    $node            =& $node[][$match[2]];
                    $node['@attrib'] = $this->parseAttrib($match[3]);
                    if ($single)
                        // single
                        $node =& $stack[$depth];
                    else
                        $depth++;
                }
                $tmp = '';
                $ptr += strlen($match[0]);
            }
            else {
                // Text node
                $tmp .= substr($text,$ptr,1);
                $ptr++;
            }
        if (strlen($tmp))
            // Append trailing text
            $node[] = $tmp;
        // Break references
        unset($node, $stack);

        return $tree;
    }
1053146
private function parse_tag(string $text){
		$res = [];
		$text = $this->clear_text($text);
		$tag_end = strpos($text, ' ');
		if ( $tag_end !== false ){
			// Get tag
			$res['tag'] = substr($text, 1, $tag_end - 1);
			if ( in_array($res['tag'], array_keys($this->tags)) ){
        if (
          $this->tag_has_text($res['tag']) &&
          ($text = substr($text, $tag_end + 1))
        ){
          $res['text'] = $this->clear_text($text);
        }
				else {
					// Get type
					if (
            $this->tag_has_type($res['tag']) &&
            ($type = $this->tag_get_type($text)) &&
            !empty($type[1])
          ){
						$res['type'] = $type[1][0];
					}
					// Get default value
					if (
            $this->tag_has_default($res['tag']) &&
            ($def = $this->tag_get_default($text)) &&
            !empty($def[1])
          ){
						$res['default'] = $def[1][0];
					}
					// Get name
					if ( isset($def[1]) ){
						$n = $def[0][1] + strlen($def[0][0]) + 1;
					}
					else if ( isset($type[1]) ){
						$n = $type[0][1] + strlen($type[0][0]) + 1;
					}
					else {
						$n = $tag_end + 1;
					}
					if (
            $this->tag_has_name($res['tag']) &&
						($name = $this->tag_get_name(substr($text, $n)))
					){
						$res['name'] = $this->clear_text($name[0][0]);
					}
					// Get description
					if ( isset($name[0]) ){
						$d = $n + $name[0][1] + strlen($name[0][0]) + 1;
					}
					else if ( isset($type[1]) ){
						$d = $type[0][1] + strlen($type[0][0]) + 1;
					}
          else {
						$d = $tag_end + 1;
					}
					if (
            $this->tag_has_desc($res['tag']) &&
            ($desc = substr($text, $d))
          ){
						$res['description'] = $this->clear_text($desc);
					}
				}

				return $res;
			}
		}
		return false;
	}
276128
public function readTab($text) {
		$pieces = explode("\n", $text);
		$result = [];
		foreach ($pieces as $piece) {
			$tmp = explode("\t", trim($piece, "\r\n"));
			$result[] = $tmp;
		}
		return $result;
	}
362733
private function normalizeText($text): string
    {
/* @TODO: try to resolve at a later date; Twig's `| raw` just returns a string, not `Markup` so we can't use that as a check
        if ($text instanceof Markup) {
            // Either came from a Redactor field (or the like) or they manually added a |raw tag. We can trust it
            $text = (string)$text;
        } else {
            // We don't trust it, so escape any HTML
            $twig = Craft::$app->view->twig;
            try {
                $text = twig_escape_filter($twig, $text);
            } catch (\Twig_Error_Runtime $e) {
                $error = $e->getMessage();
                Craft::error($error, __METHOD__);
                // We don't want unescaped text slipping through, so set the text to the error message
                $text = $error;
            }
        }
*/
        // If it's null or otherwise empty, just return an empty string
        if (empty($text)) {
            $text = '';
        }
        $text = (string)$text;

        $settings = Typogrify::$plugin->getSettings();

        if ($settings['default_escape'] === true) {
            $twig = Craft::$app->view->twig;
            $text = twig_escape_filter($twig, $text);
        }

        return $text;
    }
955189
public function autoLink($text)
    {
        $rexProtocol = '(https?://)?';
        $rexDomain = '((?:[-a-zA-Z0-9]{1,63}\.)+[-a-zA-Z0-9]{2,63}|(?:[0-9]{1,3}\.){3}[0-9]{1,3})';
        $rexPort = '(:[0-9]{1,5})?';
        $rexPath = '(/[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]*?)?';
        $rexQuery = '(\?[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
        $rexFragment = '(#[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';

        $text = preg_replace_callback(
            "&\\b$rexProtocol$rexDomain$rexPort$rexPath$rexQuery$rexFragment(?=[?.!,;:\"]?(\s|$))&",
            function ($match) {
                // Prepend http:// if no protocol specified
                $completeUrl = $match[1] ? $match[0] : "http://{$match[0]}";

                return '<a href="'.$completeUrl.'" target="_blank">'
                    .$match[2].$match[3].$match[4].'</a>';
            },
            htmlspecialchars($text)
        );

        return $text;
    }
368428
public function processText($text)
    {
        if ((string) $text != '') {
            $store = array();
            $_store = 0;
            $_offset = 0;
            if ($this->parser->strip) {
                if (strpos($text, '<') !== false) {
                    // capture html elements not to be messed with
                    $_offset = 0;
                    if (preg_match_all('#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
                                       $text, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
                        foreach ($matches as $match) {
                            $store[] = $match[ 0 ][ 0 ];
                            $_length = strlen($match[ 0 ][ 0 ]);
                            $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
                            $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);

                            $_offset += $_length - strlen($replace);
                            $_store ++;
                        }
                    }

                    $expressions = array(// replace multiple spaces between tags by a single space
                                         // can't remove them entirely, becaue that might break poorly implemented CSS display:inline-block elements
                                         '#(:SMARTY@!@|>)\s+(?=@!@SMARTY:|<)#s'                                    => '\1 \2',
                                         // remove spaces between attributes (but not in attribute values!)
                                         '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
                                         '#^\s+<#Ss'                                                               => '<',
                                         '#>\s+$#Ss'                                                               => '>',
                                         $this->stripRegEx                                                         => '');

                    $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
                    $_offset = 0;
                    if (preg_match_all('#@!@SMARTY:([0-9]+):SMARTY@!@#is', $text, $matches,
                                       PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
                        foreach ($matches as $match) {
                            $_length = strlen($match[ 0 ][ 0 ]);
                            $replace = $store[ $match[ 1 ][ 0 ] ];
                            $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);

                            $_offset += strlen($replace) - $_length;
                            $_store ++;
                        }
                    }
                } else {
                    $text = preg_replace($this->stripRegEx, '', $text);
                }
            }
            return new Smarty_Internal_ParseTree_Text($text);
        }
        return null;
    }
1041395
public function _(string $text) : string
    {
        return $this->c->translator instanceof ITranslator ? $this->c->translator->translate($text) : $text;
    }
362786
public function textToSpeech(string $text): string
    {
        if (strlen($text) > 300) {
            throw new InvalidArgumentException("Only messages under 300 characters are supported");
        }

        return $this->sendRequest("http://vaas.acapela-group.com/Services/FileMaker.mp3", [
            "prot_vers" =>  "2",
            "cl_login"  =>  $this->login,
            "cl_app"    =>  $this->application,
            "cl_pwd"    =>  $this->password,
            "req_voice" =>  "{$this->voice}22k",
            "req_spd"   =>  (string) $this->speed,
            "req_text"  =>  $text,
        ]);
    }
879997
static private function leetify( $text )
    {
        $searchMap = array( '/to/i', '/for/i', '/ate/i', '/your/i', '/you/i', '/l/i', '/e/i', '/o/i', '/a/i', '/t/i' );
        $replaceMap = array( '2', '4', '8', 'ur', 'u', '1', '3', '0', '4', '7' );

        $textBlocks = preg_split( '/(%[^ ]+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
        $newTextBlocks = array();
        foreach ( $textBlocks as $text )
        {
            if ( strlen( $text ) && $text[0] == '%' )
            {
                $newTextBlocks[] = (string) $text;
                continue;
            }
            $text = preg_replace( $searchMap, $replaceMap, $text );

            $newTextBlocks[] = (string) $text;
        }
        $text = implode( '', $newTextBlocks );
        return $text;
    }
957508
private function _convertTagsToEscapeSequences ( $text ) {
        list($patterns, $replacements) = $this->_getConvertingDictionary();
        $convertedText = preg_replace($patterns, $replacements, $text);
        return $convertedText;
    }